socializer/src/sessionmanager/session.py

242 lines
9.9 KiB
Python

# -*- coding: utf-8 -*-
""" Session object for Socializer. A session is the only object to call VK API methods, save settings and access to the cache database and sound playback mechanisms. """
import os
import logging
import languageHandler
import paths
import vkSessionHandler
import sound
from config_utils import Configuration, ConfigurationResetException
from pubsub import pub
from vk_api.exceptions import LoginRequired, VkApiError
log = logging.getLogger("session")
sessions = {}
# Save possible set of identifier keys for VK'S data types
# see https://vk.com/dev/datatypes for more information.
# I've added the Date identifier (this is a field in unix time format), for special objects (like friendship indicators) because these objects don't have an own identifier.
identifiers = ["aid", "gid", "uid", "pid", "id", "post_id", "nid", "date"]
# Different VK post types, present in the newsfeed buffer. This is useful for filtering by post and remove deleted posts.
post_types = dict(audio="audio", friend="friends", video="video", post="post_type")
def find_item(list, item):
""" Find an item in a list by taking an identifier.
@list list: A list of dict objects.
@ item dict: A dictionary containing at least an identifier.
"""
# determine the kind of identifier that we are using
global identifiers
identifier = None
for i in identifiers:
if item.has_key(i):
identifier = i
break
if identifier == None:
# if there are objects that can't be processed by lack of identifier, let's print keys for finding one.
log.exception("Can't find an identifier for the following object: %r" % (item.keys(),))
for i in list:
if i.has_key(identifier) and i[identifier] == item[identifier]:
return True
return False
class vkSession(object):
""" The only session available in socializer. Manages everything related to a model in an MVC app: calls to VK, sound handling, settings and a cache database."""
def order_buffer(self, name, data, show_nextpage):
""" Put new items on the local cache database.
@name str: The name for the buffer stored in the dictionary.
@data list: A list with items and some information about cursors.
returns the number of items that has been added in this execution"""
global post_types
first_addition = False
num = 0
if self.db.has_key(name) == False:
self.db[name] = {}
self.db[name]["items"] = []
first_addition = True
for i in data:
if i.has_key("type") and (i["type"] == "wall_photo" or i["type"] == "photo_tag" or i["type"] == "photo"):
log.debug("Skipping unsupported item... %r" % (i,))
continue
# for some reason, VK sends post data if the post has been deleted already.
# Example of this behaviour is when you upload an audio and inmediately delete the audio, VK still sends the post stating that you uploaded an audio file,
# But without the audio data, making socializer to render an empty post.
# Here we check if the post contains data of the type it advertises.
if "type" in i and post_types[i["type"]] not in i:
log.error("Detected empty post. Skipping...")
print i
continue
if find_item(self.db[name]["items"], i) == False:
# if i not in self.db[name]["items"]:
if first_addition == True or show_nextpage == True:
if self.settings["general"]["reverse_timelines"] == False: self.db[name]["items"].append(i)
else: self.db[name]["items"].insert(0, i)
else:
if self.settings["general"]["reverse_timelines"] == False: self.db[name]["items"].insert(0, i)
else: self.db[name]["items"].append(i)
num = num+1
log.debug("There are %d items in the %s buffer" % (len(self.db[name]["items"]), name))
return num
def __init__(self, session_id):
self.session_id = session_id
self.logged = False
self.settings = None
self.vk = vkSessionHandler.vkObject()
self.db = {}
self.db["users"] = {}
self.db["groups"] = {}
@property
def is_logged(self):
return self.logged
def get_configuration(self):
""" Gets settings for a session."""
file_ = "%s/session.conf" % (self.session_id,)
# try:
log.debug("Creating config file %s" % (file_,))
self.settings = Configuration(os.path.join(paths.config_path(), file_), os.path.join(paths.app_path(), "session.defaults"))
self.soundplayer = sound.soundSystem(self.settings["sound"])
# except:
# log.exception("The session configuration has failed.")
def login(self):
try:
config_filename = os.path.join(paths.config_path(), self.session_id, "vkconfig.json")
self.vk.login(self.settings["vk"]["user"], self.settings["vk"]["password"], token=self.settings["vk"]["token"], alt_token=self.settings["vk"]["use_alternative_tokens"], filename=config_filename)
self.settings["vk"]["token"] = self.vk.session_object.token["access_token"]
self.settings.write()
self.logged = True
self.get_my_data()
except ValueError:
self.settings["vk"]["user"] = ""
self.settings["vk"]["password"] = ""
self.settings.write()
pub.sendMessage("authorisation-failed")
def post_wall_status(self, message, *args, **kwargs):
""" Sends a post to an user, group or community wall."""
log.debug("Making a post to the user's wall with the following params: %r" % (kwargs,))
response = self.vk.client.wall.post(message=message, *args, **kwargs)
def get_newsfeed(self, name="newsfeed", show_nextpage=False, endpoint="", *args, **kwargs):
log.debug("Updating news feed...")
if show_nextpage == True and self.db[name].has_key("cursor"):
log.debug("user has requested previous items")
kwargs["start_from"] = self.db[name]["cursor"]
log.debug("Params for sending to vk: %r" % (kwargs,))
data = getattr(self.vk.client.newsfeed, "get")(*args, **kwargs)
if data != None:
if show_nextpage == False:
self.process_usernames(data)
# else:
# print data.keys(), len(data["items"]), data["next_from"]
num = self.order_buffer(name, data["items"], show_nextpage)
log.debug("Keys of the returned data for debug purposes: %r" % (data.keys(),))
if data.has_key("next_from"):
self.db[name]["cursor"] = data["next_from"]
return num
def get_page(self, name="", show_nextpage=False, endpoint="", *args, **kwargs):
data = None
if "audio" in endpoint and self.settings["vk"]["use_alternative_tokens"]:
log.info("Using alternative audio methods.")
c = self.vk.client_audio
else:
c = self.vk.client
if kwargs.has_key("parent_endpoint"):
p = kwargs["parent_endpoint"]
if "audio" in p and self.settings["vk"]["use_alternative_tokens"]:
log.info("Using alternative audio methods.")
c = self.vk.client_audio
kwargs.pop("parent_endpoint")
try:
p = getattr(c, p)
except AttributeError:
p = c
log.debug("Calling endpoint %s with params %r" % (p, kwargs,))
data = getattr(p, endpoint)(*args, **kwargs)
if data != None:
if type(data) == dict:
num = self.order_buffer(name, data["items"], show_nextpage)
if len(data["items"]) > 0 and data["items"][0].has_key("first_name"):
data2 = {"profiles": [], "groups": []}
for i in data["items"]:
data2["profiles"].append(i)
self.process_usernames(data2)
if data.has_key("profiles") and data.has_key("groups"):
self.process_usernames(data)
else:
num = self.order_buffer(name, data, show_nextpage)
return num
def get_messages(self, name="", *args, **kwargs):
data = self.vk.client.messages.getHistory(*args, **kwargs)
data["items"].reverse()
if data != None:
num = self.order_buffer(name, data["items"], False)
return num
def get_user_name(self, user_id, case_name="gen"):
if user_id > 0:
if self.db["users"].has_key(user_id):
if self.db["users"][user_id].has_key(case_name):
return self.db["users"][user_id][case_name]
else:
return self.db["users"][user_id]["nom"]
else:
return "no specified user"
else:
if self.db["groups"].has_key(abs(user_id)):
return self.db["groups"][abs(user_id)]["nom"]
else:
return "no specified community"
def get_users(self, user_ids=None, group_ids=None):
log.debug("Getting user information from the VK servers")
if user_ids != None:
u = self.vk.client.users.get(user_ids=user_ids, fields="uid, first_name, last_name")
for i in u:
self.db["users"][i["id"]] = u"{0} {1}".format(i["first_name"], i["last_name"])
if group_ids != None:
g = self.vk.client.groups.getById(group_ids=group_ids, fields="name")
for i in g:
self.db["groups"][i["id"]] = i["name"]
def process_usernames(self, data):
""" processes user IDS and saves them in a local storage system.
Every function wich needs to convert from an ID to user or community name will have to call the get_user_name function in this session object.
Every function that needs to save a set ot user ids for a future use needs to pass a data dictionary with a profiles key being a list of user objects.
For russian, it gets the genitive case of every name for future use."""
log.debug("Adding usernames to the local database...")
ids = ""
for i in data["profiles"]:
if self.db["users"].has_key(i["id"]) == False:
self.db["users"][i["id"]] = dict(nom=u"{0} {1}".format(i["first_name"], i["last_name"]))
ids = ids + "{0},".format(i["id"],)
gids = ""
for i in data["groups"]:
self.db["groups"][i["id"]] = dict(nom=i["name"])
gids = "{0},".format(i["id"],)
if not "ru" in languageHandler.getLanguage():
return
if ids != "":
users_genitive = self.vk.client.users.get(user_ids=ids, fields="first_name, last_name", name_case="gen")
users_instrumental = self.vk.client.users.get(user_ids=ids, fields="first_name, last_name", name_case="ins")
for i in users_genitive:
if self.db["users"].has_key(i["id"]):
self.db["users"][i["id"]]["gen"] = u"{0} {1}".format(i["first_name"], i["last_name"])
for i in users_instrumental:
if self.db["users"].has_key(i["id"]):
self.db["users"][i["id"]]["ins"] = u"{0} {1}".format(i["first_name"], i["last_name"])
def get_my_data(self):
log.debug("Getting user identifier...")
user = self.vk.client.users.get(fields="uid, first_name, last_name")
self.user_id = user[0]["id"]