Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mrDoctorWho/vk4xmpp.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormrDoctorWho <mrdoctorwho@gmail.com>2013-09-05 18:34:42 +0400
committermrDoctorWho <mrdoctorwho@gmail.com>2013-09-05 18:34:42 +0400
commit7e6006e9e3f98d2868b292d0ccdfd31b03e3e915 (patch)
tree33f0d30aab3be633dfb82ca25ce70ef57a6872ac
parent5301dc918be221712e238598a7785c75aa81669f (diff)
Added attachments support, maybe fixed bug when roster wasn't filled by friends
Added forwarded messages support.
-rw-r--r--README13
-rw-r--r--gateway.py65
-rw-r--r--library/vk_api.py377
-rw-r--r--locales/locale.ru2
4 files changed, 252 insertions, 205 deletions
diff --git a/README b/README
new file mode 100644
index 0000000..b7bd726
--- /dev/null
+++ b/README
@@ -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
diff --git a/gateway.py b/gateway.py
index 3db3c89..ba92ff1 100644
--- a/gateway.py
+++ b/gateway.py
@@ -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