diff options
-rw-r--r-- | README | 13 | ||||
-rw-r--r-- | gateway.py | 65 | ||||
-rw-r--r-- | library/vk_api.py | 377 | ||||
-rw-r--r-- | locales/locale.ru | 2 |
4 files changed, 252 insertions, 205 deletions
@@ -0,0 +1,13 @@ +Транспорт. Из VK в XMPP и обратно. + +Написан на языке программирования Python, все используемые библиотеки содержит в себе. +Находится в состоянии тестирования. Требует Python 2.7. + +Установка стандартна как и для любого другого транспорта. Только вот переименуйте Config_example.txt в Config.txt и заполните его. +На время тестирования рекомендуется заглядывать в папку транспорта и сообщать разработчику об ошибках (тела ошибок хранятся в папке crash). + +Запуск: +python ./gateway.py + +Обратиться к разработчику в сети xmpp можно в конференции xmpp:simpleapps@conference.jabber.ru?join +© simpleApps, 2013.
\ No newline at end of file @@ -6,6 +6,7 @@ # Program published under MIT license. import os, sys, time, json, signal, urllib, socket, traceback, threading +from datetime import datetime from math import ceil if not hasattr(sys, "argv") or not sys.argv[0]: sys.argv = ["."] @@ -29,6 +30,7 @@ import xmpp ## other. from itypes import Database, Number +from webtools import * from writer import * from stext import * from stext import _ @@ -67,7 +69,7 @@ setVars(DefLang, __file__) import logging logger = logging.getLogger("vk4xmpp") -logger.setLevel(logging.INFO) +logger.setLevel(logging.DEBUG) loggerHandler = logging.FileHandler("vk4xmpp.log") Formatter = logging.Formatter("%(asctime)s:%(levelname)s:%(name)s %(message)s", "[%d.%m.%Y %H:%M:%S]") loggerHandler.setFormatter(Formatter) @@ -179,9 +181,9 @@ class VKLogin(object): try: return func(method, args) except api.apiError as e: - logger.error("VKLogin: apiError %s for user %s" % (e.message, self.jidFrom)) if e.message == "Logged out": - pass + return {} + logger.error("VKLogin: apiError %s for user %s" % (e.message, self.jidFrom)) except api.captchaNeeded: logger.error("VKLogin: running captcha challenge for %s" % self.jidFrom) self.captchaChallenge() @@ -213,11 +215,10 @@ class VKLogin(object): def getFriends(self, fields = "screen_name"): friendsRaw = self.method("friends.get", {"fields": fields}) # friends.getOnline friendsDict = {} - logger.debug("VKLogin: requesing friends for %s" % self.jidFrom) if friendsRaw: for friend in friendsRaw: id = friend["uid"] - name = " ".join([friend["first_name"], friend["last_name"]]) + name = u"%s %s" % (friend["first_name"], friend["last_name"]) try: friendsDict[id] = {"name": name, "online": friend["online"]} friendsDict[id]["photo"] = friend.get("photo_200_orig", URL_VCARD_NO_IMAGE) @@ -231,7 +232,6 @@ class VKLogin(object): self.method("messages.markAsRead", {"message_ids": list}) def getMessages(self, lastjoinTime = 0, count = 5, lastMsgID = 0): - logger.debug("VKLogin: requesing messages for %s" % self.jidFrom) values = {"out": 0, "time_offset": lastjoinTime, "filters": 1, "count": count} if lastMsgID: @@ -253,10 +253,10 @@ class tUser(object): if data: self.username, self.password = data self.cl = cl + self.friends = {} self.auth = False self.token = False self.fullJID = False - self.friends = False self.lastStatus = False self.lastMsgID = False self.rosterSet = False @@ -340,10 +340,10 @@ class tUser(object): def init(self, force = False): logger.debug("tUser: called init for user %s" % self.jUser) - friends = self.vk.getFriends() - if friends and not self.rosterSet or force: + self.friends = self.vk.getFriends() + if self.friends and not self.rosterSet or force: logger.debug("tUser: calling subscribe with force:%s for %s" % (force, self.jUser)) - self.rosterSubscribe(friends) + self.rosterSubscribe(self.friends) threadRun(self.sendInitPresence) threadRun(self.sendMessages) # is it valid? @@ -371,7 +371,30 @@ class tUser(object): for message in messages: read.append(str(message.get("mid", 0))) fromjid = "%s@%s" % (message["uid"], TransportID) - body = message["body"].replace("<br>", "\n") + body = uHTML(message["body"]) + if message.has_key("attachments"): + body += _("\nAttachments:") + attachments = message["attachments"] + for att in attachments: + key = att.get("type", {}) + if att[key].has_key("url"): + body += "\n" + att[key]["url"] + if message.has_key("fwd_messages"): + body += _("\nForward messages") + fwd_messages = sorted(message["fwd_messages"], lambda a, b: a["date"] - b["date"]) + for fwd in fwd_messages: + idFrom = fwd["uid"] + date = fwd["date"] + fwdBody = uHTML(fwd["body"]) + date = datetime.fromtimestamp(date).strftime("%d.%m.%Y %H:%M:%S") + if idFrom not in self.friends: + name = self.vk.method("users.get", {"fields": "screen_name", "user_ids": idFrom}) + if name: + name = name.pop() + name = u"%s %s" % (name["first_name"], name["last_name"]) + else: + name = self.friends[idFrom] + body += "\n[%s] <%s> %s" % (date, name, fwdBody) msgSend(self.cl, self.jUser, body, fromjid, message["date"]) self.vk.msgMarkAsRead(read) if UseLastMessageID: @@ -381,15 +404,18 @@ class tUser(object): def rosterSubscribe(self, dist = {}): Presence = xmpp.Presence(self.jUser, "subscribe") Presence.setTag("nick", namespace = xmpp.NS_NICK) - for id in dist.keys() + [TransportID]: - nickName = self.friends.get(id, {}).get("name") + for id in dist.keys(): + nickName = self.friends[id]["name"] Presence.setTagData("nick", nickName) Presence.setFrom(vk2xmpp(id)) Sender(self.cl, Presence) time.sleep(0.2) - self.rosterSet = True - with Database(DatabaseFile, Semaphore) as db: - db("update users set rosterSet=? where jid=?", (self.rosterSet, self.jUser)) + Presence.setFrom(TransportID) + Sender(self.cl, Presence) + if dist: + self.rosterSet = True + with Database(DatabaseFile, Semaphore) as db: + db("update users set rosterSet=? where jid=?", (self.rosterSet, self.jUser)) def tryAgain(self): if not self.auth: @@ -403,6 +429,7 @@ class tUser(object): def Sender(cl, stanza): try: cl.send(stanza) + time.sleep(0.001) except IOError: logger.error("Panic: Couldn't send stanza: %s" % str(stanza)) except: @@ -509,7 +536,7 @@ def prsHandler(cl, prs): else: raise xmpp.NodeProcessed() - elif pType == "unavailable": + elif pType == "unavailable" and Class.lastStatus != pType: Sender(cl, xmpp.Presence(jidFromStr, "unavailable", frm = TransportID)) Class.vk.disconnect() @@ -524,8 +551,6 @@ def prsHandler(cl, prs): if id in Class.friends: if Class.friends[id]["online"]: Sender(cl, xmpp.Presence(jidFrom, frm = jidTo)) -# else: -# raise xmpp.NodeProcessed() Class.lastStatus = pType def iqBuildError(stanza, error = None, text = None): @@ -955,7 +980,7 @@ def exit(signal = None, frame = None): # LETS BURN CPU AT LAST TIME! for id in friends: jid = vk2xmpp(id) Presence.setFrom(jid) - Send(Component, Presence) + Sender(Component, Presence) Print(".", False) Print("\n") os._exit(1) diff --git a/library/vk_api.py b/library/vk_api.py index 8759948..e1025db 100644 --- a/library/vk_api.py +++ b/library/vk_api.py @@ -1,185 +1,192 @@ -# /* coding: utf-8 */
-# based on VKApi module by Kirill Python
-# Modifications © simpleApps.
-
-import re, time
-import requests, webtools
-
-class VkApi:
- def __init__(self, number, password = None,
- sid = None, token= None, app_id = 3789129,
- scope = 69634, proxies = None):
- self.password = password
- self.number = number
-
- self.sid = sid
- self.token = token
- self.captcha = {}
- self.settings = {}
- self.lastMethod = None
-
- self.app_id = app_id
- self.scope = scope
-
- self.http = requests.Session()
- self.http.proxies = proxies
- self.http.headers = {"User-agent": "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:21.0) Gecko/20130309 Firefox/21.0",
- "Accept-Language":"ru-RU, utf-8"}
- self.http.verify = False
-
-
- def vk_login(self, cSid=None, cKey = None):
- url = "https://login.vk.com/"
- values = {"act": "login",
- "utf8": "1",
- "email": self.number,
- "pass": self.password}
-
- if cSid and cKey:
- values["captcha_sid"] = cSid
- values["captcha_key"] = cKey
-
- self.http.cookies.clear()
- response = self.http.post(url, values)
- if "remixsid" in self.http.cookies:
- remixsid = self.http.cookies["remixsid"]
- self.settings["remixsid"] = remixsid
-
- self.settings["forapilogin"] = {"p": self.http.cookies["p"],
- "l": self.http.cookies["l"] }
-
- self.sid = remixsid
-
- elif "sid=" in response.url:
- raise authError("Authorization error (captcha)")
- else:
- raise authError("Authorization error (bad password)")
-
- if "security_check" in response.url:
- number_hash = regexp(r"security_check.*?hash: '(.*?)'\};", response.text)[0]
-
- code = self.number[2:-2]
- if len(self.number) == 12:
- if not self.number.startswith("+"):
- code = self.number[3:-2] # may be +375123456789
-
- elif len(self.number) == 13: # so we need 1234567
- if self.number.startswith("+"):
- code = self.number[4:-2]
-
- values = {"act": "security_check",
- "al": "1",
- "al_page": "3",
- "code": code,
- "hash": number_hash,
- "to": ""}
- response = self.http.post("https://vk.com/login.php", values)
- if response.text.split("<!>")[4] == "4":
- return
-
- raise authError("Incorrect number")
-
- def check_sid(self):
- """Valiating cookies remixsid"""
- if self.sid:
- url = "https://vk.com/feed2.php"
- self.http.cookies.update({
- "remixsid": self.sid,
- "remixlang": "0",
- "remixsslsid": "1"
- })
-
- response = self.http.get(url).json()
-
- if response["user"]["id"] != -1:
- return response
-
- def api_login(self):
- url = "https://oauth.vk.com/authorize"
- values = {"display": "mobile",
- "scope": self.scope,
- "client_id": self.app_id,
- "response_type": "token",
- "redirect_uri": "https://oauth.vk.com/blank.html"}
-
- token = None
- GET = self.http.get(url, params = values)
- getUrl = GET.url
- if "access_token" in getUrl:
- token = getUrl.split("=")[1].split("&")[0]
- else:
- POST = webtools.getTagArg("form method=\"post\"", "action", GET.text, "form")
- if POST:
- response = self.http.post(POST)
- token = response.url.split("=")[1].split("&")[0]
- else:
- raise authError("ApiError")
- self.token = token
-
-
- def method(self, method, values={}):
- url = "https://api.vk.com/method/%s" % method
- values["access_token"] = self.token
- if self.captcha and self.captcha.has_key("key"):
- values["captcha_sid"] = self.captcha["sid"]
- values["captcha_key"] = self.captcha["key"]
- self.lastMethod = (method, values)
-## print "method %s with values %s" % (method, str(values))
- ## This code can be useful when we're loaded too high
- try:
- json = self.http.post(url, values).json()
- except requests.ConnectionError:
- try:
- time.sleep(1)
- json = self.http.post(url, values).json()
- except:
- return {}
-## print "response:%s"% str(json)
- if json.has_key("response"):
- return json["response"]
-
- elif json.has_key("error"):
- error = json["error"]
- eCode = error["error_code"]
- if eCode == 6: # too fast
- time.sleep(3)
- return self.method(method, values)
- elif eCode == 5: # auth failed
- raise apiError("Logged out")
- if eCode == 14:
- if "captcha_sid" in error: # maybe we need check if exists self.captcha
- self.captcha = {"sid": error["captcha_sid"], "img": error["captcha_img"]}
- raise captchaNeeded
- raise apiError(json["error"])
-
- def retry(self):
- if self.lastMethod:
- return self.method(*self.lastMethod)
-
-def regexp(reg, string, findall = 1):
- u""" Поиск по регулярке """
-
- reg = re.compile(reg, re.IGNORECASE | re.DOTALL)
- if findall:
- reg = reg.findall(string)
- else:
- return reg.search(string)
- return reg
-
-
-class vkApiError(Exception):
- pass
-
-
-class authError(vkApiError):
- pass
-
-
-class apiError(vkApiError):
- pass
-
-class tokenError(vkApiError):
- pass
-
-class captchaNeeded(vkApiError):
- pass
\ No newline at end of file +# /* coding: utf-8 */ +# based on VKApi module by Kirill Python +# Modifications © simpleApps. + +import re, time +import requests, urllib, webtools + +class VkApi: + def __init__(self, number, password = None, + sid = None, token= None, app_id = 3789129, + scope = 69634, proxies = None): + self.password = password + self.number = number + + self.sid = sid + self.token = token + self.captcha = {} + self.settings = {} + self.last = [] + self.lastMethod = None + + self.app_id = app_id + self.scope = scope + + self.http = requests.Session() + self.http.proxies = proxies + self.http.headers = {"User-agent": "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:21.0) Gecko/20130309 Firefox/21.0", + "Accept-Language":"ru-RU, utf-8"} + self.http.verify = False + + + def vk_login(self, cSid=None, cKey = None): + url = "https://login.vk.com/" + values = {"act": "login", + "utf8": "1", + "email": self.number, + "pass": self.password} + + if cSid and cKey: + values["captcha_sid"] = cSid + values["captcha_key"] = cKey + + self.http.cookies.clear() + response = self.http.post(url, values) + if "remixsid" in self.http.cookies: + remixsid = self.http.cookies["remixsid"] + self.settings["remixsid"] = remixsid + + self.settings["forapilogin"] = {"p": self.http.cookies["p"], + "l": self.http.cookies["l"] } + + self.sid = remixsid + + elif "sid=" in response.url: + raise authError("Authorization error (captcha)") + else: + raise authError("Authorization error (bad password)") + + if "security_check" in response.url: + number_hash = regexp(r"security_check.*?hash: '(.*?)'\};", response.text)[0] + + code = self.number[2:-2] + if len(self.number) == 12: + if not self.number.startswith("+"): + code = self.number[3:-2] # may be +375123456789 + + elif len(self.number) == 13: # so we need 1234567 + if self.number.startswith("+"): + code = self.number[4:-2] + + values = {"act": "security_check", + "al": "1", + "al_page": "3", + "code": code, + "hash": number_hash, + "to": ""} + response = self.http.post("https://vk.com/login.php", values) + if response.text.split("<!>")[4] == "4": + return + + raise authError("Incorrect number") + + def check_sid(self): + """Valiating cookies remixsid""" + if self.sid: + url = "https://vk.com/feed2.php" + self.http.cookies.update({ + "remixsid": self.sid, + "remixlang": "0", + "remixsslsid": "1" + }) + + response = self.http.get(url).json() + + if response["user"]["id"] != -1: + return response + + def api_login(self): + url = "https://oauth.vk.com/authorize" + values = {"display": "mobile", + "scope": self.scope, + "client_id": self.app_id, + "response_type": "token", + "redirect_uri": "https://oauth.vk.com/blank.html"} + + token = None + GET = self.http.get(url, data = values) + getUrl = GET.url + if "access_token" in getUrl: + token = getUrl.split("=")[1].split("&")[0] + else: + POST = webtools.getTagArg("form method=\"post\"", "action", GET.text, "form") + if POST: + response = self.http.post(POST) + token = response.url.split("=")[1].split("&")[0] + else: + raise authError("ApiError") + self.token = token + + + def method(self, method, values={}): + url = "https://api.vk.com/method/%s" % method + values["access_token"] = self.token + if self.captcha and self.captcha.has_key("key"): + values["captcha_sid"] = self.captcha["sid"] + values["captcha_key"] = self.captcha["key"] + self.lastMethod = (method, values) + ## This code can be useful when we're loaded too high + self.last.append(time.time()) + if len(self.last) > 2: + if (self.last.pop() - self.last.pop(0)) < 1.1: +## print "sleeping because too fat %s" % str(self.last) + time.sleep(1) + try: + json = self.http.post(url, values).json() + except requests.ConnectionError: + try: + time.sleep(1) + json = self.http.post(url, values).json() + except: + return {} +## if method == "messages.get": +## print "method %s with values %s" % (method, str(values)) +## print "response for method %s: %s" % (method, str(json)) + if json.has_key("response"): + return json["response"] + + elif json.has_key("error"): + error = json["error"] + eCode = error["error_code"] + if eCode == 6: # too fast + time.sleep(3) + return self.method(method, values) + elif eCode == 5: # auth failed + raise apiError("Logged out") + if eCode == 14: + if "captcha_sid" in error: # maybe we need check if exists self.captcha + self.captcha = {"sid": error["captcha_sid"], "img": error["captcha_img"]} + raise captchaNeeded + raise apiError(json["error"]) + + def retry(self): + if self.lastMethod: + return self.method(*self.lastMethod) + +def regexp(reg, string, findall = 1): + u""" Поиск по регулярке """ + + reg = re.compile(reg, re.IGNORECASE | re.DOTALL) + if findall: + reg = reg.findall(string) + else: + return reg.search(string) + return reg + + +class vkApiError(Exception): + pass + + +class authError(vkApiError): + pass + + +class apiError(vkApiError): + pass + +class tokenError(vkApiError): + pass + +class captchaNeeded(vkApiError): + pass diff --git a/locales/locale.ru b/locales/locale.ru index 2767f37..7e6f707 100644 --- a/locales/locale.ru +++ b/locales/locale.ru @@ -19,3 +19,5 @@ User is not your friend.=Пользователь не ваш друг! Your friend-list is null.=Ваш список друзей пуст! You're not registered for this action.=Вы должны быть зарегистрированы для этого дейстия. Auth failed! Please register again. This incident will be reported.=Авторизация не удалась. Пожалуйста, зарегистрируйтесь заново. Об этом инциденте будет сообщено. +\LForward messages:=\LПересланные сообщения: +\LAttachments:=\LВложения:
\ No newline at end of file |