357 lines
14 KiB
Python
357 lines
14 KiB
Python
# -*- coding: utf-8 -*-
|
|
import time
|
|
import arrow
|
|
import languageHandler
|
|
import paths
|
|
import vkSessionHandler
|
|
import logging
|
|
import utils
|
|
import sound
|
|
from config_utils import Configuration, ConfigurationResetException
|
|
from pubsub import pub
|
|
from vk.exceptions import VkAPIError
|
|
|
|
log = logging.getLogger("session")
|
|
|
|
sessions = {}
|
|
|
|
# Saves 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 doesn't have an own identifier.
|
|
identifiers = ["aid", "gid", "uid", "pid", "id", "post_id", "nid", "date"]
|
|
|
|
def find_item(list, item):
|
|
""" Finds an item in a list by taking an identifier"""
|
|
# determines 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
|
|
|
|
def add_attachment(attachment):
|
|
""" Adds information about attachment files in posts. It only adds the text, I mean, no attachment file is added here.
|
|
This will produce a result like 'Title of a web page: http://url.xxx', etc."""
|
|
msg = u""
|
|
if attachment["type"] == "link":
|
|
msg = u"{0}: {1}".format(attachment["link"]["title"], attachment["link"]["url"])
|
|
elif attachment["type"] == "photo":
|
|
msg = attachment["photo"]["text"]
|
|
if msg == "":
|
|
return _(u"photo with no description available")
|
|
elif attachment["type"] == "video":
|
|
msg = _(u"video: {0}").format(attachment["video"]["title"],)
|
|
return msg
|
|
|
|
def add_text(status):
|
|
""" This shorts the text to 140 characters for displaying it in the list control."""
|
|
message = ""
|
|
if status.has_key("copy_history"):
|
|
txt = status["copy_history"][0]["text"]
|
|
else:
|
|
txt = status["text"]
|
|
if len(txt) < 140:
|
|
message = utils.clean_text(txt)
|
|
else:
|
|
message = utils.clean_text(txt[:139])
|
|
return message
|
|
|
|
def compose_person(status, session):
|
|
if status.has_key("last_seen"):
|
|
original_date = arrow.get(status["last_seen"]["time"])
|
|
# Translators: This is the date of last seen
|
|
last_seen = _(u"{0}").format(original_date.humanize(locale=languageHandler.getLanguage()),)
|
|
elif status.has_key("last_seen") == False and status.has_key("deactivated"):
|
|
last_seen = _(u"Account deactivated")
|
|
return [u"{0} {1}".format(status["first_name"], status["last_name"]), last_seen]
|
|
|
|
def compose_new(status, session):
|
|
""" This method is used to compose an item of the news feed."""
|
|
user = session.get_user_name(status["source_id"], case_name="nom")
|
|
if status.has_key("copy_history"):
|
|
user = _(u"{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_history"][0]["owner_id"]))
|
|
message = ""
|
|
original_date = arrow.get(status["date"])
|
|
created_at = original_date.humanize(locale=languageHandler.getLanguage())
|
|
if status["type"] == "post":
|
|
message += add_text(status)
|
|
if status.has_key("attachment") and len(status["attachment"]) > 0:
|
|
message += add_attachment(status["attachment"])
|
|
if message == "":
|
|
message = "no description available"
|
|
elif status["type"] == "audio":
|
|
# removes deleted audios.
|
|
status["audio"] = clean_audio(status["audio"])
|
|
if status["audio"]["count"] == 1:
|
|
message = _(u"{0} has added an audio: {1}").format(user, u", ".join(compose_audio(status["audio"]["items"][0], session)),)
|
|
else:
|
|
prem = ""
|
|
for i in xrange(0, status["audio"]["count"]):
|
|
composed_audio = compose_audio(status["audio"]["items"][i], session)
|
|
prem += u"{0} - {1}, ".format(composed_audio[0], composed_audio[1])
|
|
message = _(u"{0} has added {1} audios: {2}").format(user, status["audio"]["count"], prem)
|
|
elif status["type"] == "friend":
|
|
msg_users = u""
|
|
if status.has_key("friends"):
|
|
for i in status["friends"]["items"]:
|
|
msg_users = msg_users + u"{0}, ".format(session.get_user_name(i["user_id"], "nom"))
|
|
else:
|
|
print status.keys()
|
|
message = _(u"{0} hadded friends: {1}").format(user, msg_users)
|
|
elif status["type"] == "video":
|
|
if status["video"]["count"] == 1:
|
|
message = _(u"{0} has added a video: {1}").format(user, u", ".join(compose_video(status["video"]["items"][0], session)),)
|
|
else:
|
|
prem = ""
|
|
for i in xrange(0, status["video"]["count"]):
|
|
composed_video = compose_video(status["video"]["items"][i], session)
|
|
prem += u"{0} - {1}, ".format(composed_video[0], composed_video[1])
|
|
message = _(u"{0} has added {1} videos: {2}").format(user, status["video"]["count"], prem)
|
|
else:
|
|
if status["type"] != "post": print status
|
|
return [user, message, created_at]
|
|
|
|
def clean_audio(audio):
|
|
for i in audio["items"][:]:
|
|
if type(i) == bool:
|
|
audio["items"].remove(i)
|
|
audio["count"] = audio["count"] -1
|
|
return audio
|
|
|
|
def compose_message(message, session):
|
|
user = session.get_user_name(message["from_id"], "nom")
|
|
original_date = arrow.get(message["date"])
|
|
created_at = original_date.format(_(u"dddd, MMMM D, YYYY H:m:s"), locale=languageHandler.getLanguage())
|
|
body = message["body"]
|
|
return [u"{2}, {0} {1}".format(body, created_at, user)]
|
|
|
|
def compose_status(status, session):
|
|
user = session.get_user_name(status["from_id"], "nom")
|
|
if status.has_key("copy_history"):
|
|
user = _(u"{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_history"][0]["owner_id"]))
|
|
message = ""
|
|
original_date = arrow.get(status["date"])
|
|
created_at = original_date.humanize(locale=languageHandler.getLanguage())
|
|
if status.has_key("copy_owner_id"):
|
|
user = _(u"{0} has shared the {1}'s post").format(user, session.get_user_name(status["copy_owner_id"]))
|
|
if status["post_type"] == "post" or status["post_type"] == "copy":
|
|
message += add_text(status)
|
|
if status.has_key("attachment") and len(status["attachment"]) > 0:
|
|
message += add_attachment(status["attachment"])
|
|
if message == "":
|
|
message = "no description available"
|
|
return [user, message, created_at]
|
|
|
|
def compose_audio(audio, session=None):
|
|
if audio == False: return [_(u"Audio removed from library"), "", ""]
|
|
return [audio["title"], audio["artist"], utils.seconds_to_string(audio["duration"])]
|
|
|
|
def compose_video(video, session=None):
|
|
if video == False: return [_(u"Audio removed from library"), "", ""]
|
|
return [video["title"], video["description"], utils.seconds_to_string(video["duration"])]
|
|
|
|
class vkSession(object):
|
|
|
|
def order_buffer(self, name, data, show_nextpage):
|
|
|
|
""" Put new items on the local database. Useful for cursored buffers
|
|
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"""
|
|
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
|
|
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(paths.config_path(file_), paths.app_path("session.defaults"))
|
|
self.soundplayer = sound.soundSystem(self.settings["sound"])
|
|
# except:
|
|
# log.exception("The session configuration has failed.")
|
|
|
|
def login(self):
|
|
""" Login using credentials from settings.
|
|
if the user account isn't authorised, it'll call self.authorise() before login.
|
|
If the access_token has expired, it will call authorise() too, for getting a new access token."""
|
|
|
|
if self.settings["vk"]["token"] != None:
|
|
# Handle special case where you get identical access tokens for two IP addresses.
|
|
# See https://github.com/manuelcortez/socializer/issues/11
|
|
try:
|
|
result = self.vk.login_access_token(self.settings["vk"]["token"])
|
|
self.logged = True
|
|
log.debug("Logged.")
|
|
if result == False:
|
|
self.authorise()
|
|
except VkAPIError as err:
|
|
if err.code == 5:
|
|
self.authorise()
|
|
else:
|
|
self.authorise()
|
|
self.get_my_data()
|
|
|
|
def authorise(self):
|
|
try:
|
|
self.vk.login(self.settings["vk"]["user"], self.settings["vk"]["password"])
|
|
self.settings["vk"]["token"] = self.vk.client._session.access_token
|
|
self.settings.write()
|
|
except:
|
|
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 kwargs.has_key("parent_endpoint"):
|
|
p = kwargs["parent_endpoint"]
|
|
kwargs.pop("parent_endpoint")
|
|
p = getattr(self.vk.client, p)
|
|
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)
|
|
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"]
|
|
# self.db["users"][self.user_id] = u"{0} {1}".format(user[0]["first_name"], user[0]["last_name"])
|