[SVN] release 0.4.0

[SVN] all comments in code now in English; Sofia ♥ Fuck you with this barbarian language in code comments! (no, no fuck please)
[FIX] monkey-patched telethon library to fix updates receiving in some sessions (fix github issue #686; commit b20aa0ccc91b3d767c26702f3611c44772d87f5a)
[FIX] fixed "normal" quotation, such as `> text`, in previous releases it was intended to be message id to reply
[FIX] now processing bots in contact list, in roster with `b` prefix
[FIX] fixed message editing and deleting in supergroups
[FIX] now removing old telegram session from database when initiating a new one for current JID
[FIX] fixed group creating; group, supergroup, channels creating is now OK and tested
[FIX] fixed processing bot in roster; now it does not crashes gateway
[UPD] default status update interval is now 30
[UPD] slighly (and finally) changed behaviour of presences:

      `Last seen recently` = `dnd (do not disturb)`, because user that enabled this privacy settings does not want you to disturb
      `Last seen a long time ago` = `xa (extended away)`, because it is 'long' away
      'Last seen at %date%` = `away`, because user just got away

[ADD] basic interaction with bots (now only with text commands)
This commit is contained in:
annelin
2018-07-03 00:41:15 +00:00
parent b17870cbdd
commit 2f2d534f09
6 changed files with 144 additions and 133 deletions

View File

@@ -30,6 +30,7 @@ import time
from xmpp_tg.utils import localtime, display_tg_name
from .utils import var_dump
import xmpp_tg.monkey
import traceback
@@ -59,6 +60,11 @@ class TelegramGateClient(TelegramClient):
def xmpp_update_handler(self, obj):
"""
Main function: Telegram update handler.
:param media:
:return:
"""
# print("We have received update for <%s>" % self.jid)
# print(obj)
@@ -69,19 +75,6 @@ class TelegramGateClient(TelegramClient):
self._media_thread.start()
self._status_update_thread.start()
'''
Боты
Сделать запоминание ростера в бд
Сделать лучше хендлинг ошибок
Доделать все типы информационных сообщений
Сделать джойны по линкам в чаты/каналы
Сделать поиск и добавление пользователей
Сделать листание истории
Сделать отправку всех непрочтенных сообщений при входе
'''
# Здесь будет очень длинный пиздец ^__^
nl = '\n'
try:
@@ -123,8 +116,10 @@ class TelegramGateClient(TelegramClient):
# detect from id
if is_user:
cid = obj.message.from_id
peer = InputPeerUser(cid, self.xmpp_gate.tg_dialogs[self.jid]['users'][cid].access_hash) if cid in self.xmpp_gate.tg_dialogs[self.jid]['users'] else None
prefix = 'u'
user = self._get_user_information(cid)
peer = InputPeerUser(user.id, user.access_hash)
prefix = 'u'
prefix = 'b' if user.bot else prefix
elif is_group:
cid = obj.message.to_id.chat_id
peer = InputPeerChat(cid)
@@ -181,9 +176,9 @@ class TelegramGateClient(TelegramClient):
if type(obj.status) is UserStatusOnline:
self._status_updates[str(obj.user_id)] = { 'status': None, 'message': 'Online' }
elif type(obj.status) is UserStatusOffline:
self._status_updates[str(obj.user_id)] = { 'status': 'xa', 'message': localtime(obj.status.was_online).strftime('Last seen at %H:%M %d/%m/%Y') }
self._status_updates[str(obj.user_id)] = { 'status': 'away', 'message': localtime(obj.status.was_online).strftime('Last seen at %H:%M %d/%m/%Y') }
elif type(obj.status) is UserStatusRecently:
self._status_updates[str(obj.user_id)] = { 'status': 'away', 'message': 'Last seen recently' }
self._status_updates[str(obj.user_id)] = { 'status': 'dnd', 'message': 'Last seen recently' }
else:
pass
@@ -201,7 +196,7 @@ class TelegramGateClient(TelegramClient):
def generate_media_link(self, media):
"""
Генерирует будующее имя и ссылку на скачиваемое медиа-вложения из сообщения
Generates download link from media object
:param media:
:return:
"""
@@ -228,7 +223,7 @@ class TelegramGateClient(TelegramClient):
@staticmethod
def get_document_attribute(attributes, match):
"""
Находит заданных аттрибут в списке. Используется при разборе медиа-вложений типа Документ.
Get document attribute.
:param attributes:
:param match:
:return:
@@ -248,24 +243,23 @@ class TelegramGateClient(TelegramClient):
self.xmpp_gate.tg_dialogs[self.jid]['users'][uid] = entity
return entity
else:
return {'first_name': 'Unknown', 'last_name': 'user', 'access_hash': -1, 'id': 0}
return {'first_name': 'Unknown', 'last_name': 'user', 'access_hash': -1, 'id': 0, 'bot': False}
def _process_forward_msg(self, message):
"""
Обрабатывает информацию в пересланном сообщении (от кого оно и/или из какого канала). Требует дополнительно
предоставление информации об пользователях/каналах.
Process forward message to find out from what user message is forwarded.
:param message:
:param users:
:param channels:
:return:
"""
if message.fwd_from.from_id: # От пользователя
if message.fwd_from.from_id: # from user
usr = self._get_user_information(message.fwd_from.from_id)
fwd_from = display_tg_name(usr)
if message.fwd_from.channel_id: # От канала
if message.fwd_from.channel_id: # from channel
fwd_from = 'Channel {}'.format(message.fwd_from.channel_id)
# let's construct
@@ -274,74 +268,72 @@ class TelegramGateClient(TelegramClient):
def _process_media_msg(self, media):
"""
Обрабатывает медиа-вложения в сообщениях. Добавляет их в очередь на загрузку. Производит разбор с генерацию
готового для вывода сообщения с информацией о медиа и сгенерированной ссылкой на него.
Process message with media.
:param media:
:return:
"""
msg = ''
if type(media) is MessageMediaDocument: # Документ или замаскированная сущность
if type(media) is MessageMediaDocument: # document
attributes = media.document.attributes
attributes_types = [type(a) for a in attributes] # Документами могут быть разные вещи и иметь аттрибуты
attributes_types = [type(a) for a in attributes]
size_text = '|Size: {:.2f} Mb'.format(media.document.size / 1024 / 1024)
if media.document.size > self.xmpp_gate.config['media_max_download_size']: # Не загружаем большие файлы
g_link = {'link': 'File is too big to be downloaded via Telegram <---> XMPP Gateway. Sorry.'}
else:
g_link = self.generate_media_link(media) # Добавляем файл в очередь на загрузку в отдельном потоке
if media.document.size > self.xmpp_gate.config['media_max_download_size']: # too big file
g_link = {'link': 'File is too big to be downloaded via this gateway. Sorry.'}
else: # add it to download queue if everything is ok
g_link = self.generate_media_link(media)
self._media_queue.put({'media': media, 'file': g_link['name']})
attr_fn = self.get_document_attribute(attributes, DocumentAttributeFilename)
if attr_fn: # Если есть оригинальное имя файла, то выводим
if attr_fn: # file has filename attrib
msg = '[FileName:{}{}] {}'.format(attr_fn.file_name, size_text, g_link['link'])
else:
msg = g_link['link']
if DocumentAttributeSticker in attributes_types: # Стикер
if DocumentAttributeSticker in attributes_types: # sticker
smile = self.get_document_attribute(attributes, DocumentAttributeSticker).alt
msg = '[Sticker {}] {}'.format(smile, g_link['link']) # У стикеров свой формат вывода
elif DocumentAttributeAudio in attributes_types: # Аудио файл / Голосовое сообщение
msg = '[Sticker {}] {}'.format(smile, g_link['link'])
elif DocumentAttributeAudio in attributes_types: # audio file
attr_a = self.get_document_attribute(attributes, DocumentAttributeAudio)
if attr_a.voice: # Голосовое сообщение
msg = '[VoiceMessage|{} sec] {}'.format(attr_a.duration, g_link['link']) # Тоже свой формат
else: # Приложенный аудиофайл, добавляем возможную информацию из его тегов
if attr_a.voice: # voicemessage
msg = '[VoiceMessage|{} sec] {}'.format(attr_a.duration, g_link['link'])
else: # other audio
attr_f = self.get_document_attribute(attributes, DocumentAttributeFilename)
msg = '[Audio|File:{}{}|Performer:{}|Title:{}|Duration:{} sec] {}' \
.format(attr_f.file_name, size_text, attr_a.performer, attr_a.title,
attr_a.duration, g_link['link'])
elif DocumentAttributeVideo in attributes_types: # Видео
elif DocumentAttributeVideo in attributes_types: # video
video_type = 'Video'
video_file = ''
caption = ''
if DocumentAttributeAnimated in attributes_types: # Проверка на "gif"
if DocumentAttributeAnimated in attributes_types: # it is "gif"
video_type = 'AnimatedVideo'
if DocumentAttributeFilename in attributes_types: # Если есть оригинальное имя файла - указываем
if DocumentAttributeFilename in attributes_types: # file has filename attrib
attr_v = self.get_document_attribute(attributes, DocumentAttributeFilename)
video_file = '|File:{}'.format(attr_v.file_name)
if hasattr(media, 'caption'):
caption = media.caption + ' '
# Тоже свой формат
msg = '[{}{}{}] {}{}'.format(video_type, video_file, size_text, caption, g_link['link'])
elif type(media) is MessageMediaPhoto: # Фотография (сжатая, jpeg)
elif type(media) is MessageMediaPhoto: # photo (jpg)
g_link = self.generate_media_link(media)
msg = g_link['link']
self._media_queue.put({'media': media, 'file': g_link['name']})
if hasattr(media, 'caption'): # Если есть описание - указываем
if hasattr(media, 'caption'): # caption
msg = '{} {}'.format(media.caption, msg)
elif type(media) is MessageMediaContact: # Контакт (с номером)
elif type(media) is MessageMediaContact: # contact
msg = 'First name: {} / Last name: {} / Phone: {}'\
.format(media.first_name, media.last_name, media.phone_number)
elif type(media) in [MessageMediaGeo, MessageMediaVenue]: # Адрес на карте
elif type(media) in [MessageMediaGeo, MessageMediaVenue]: # address
map_link_template = 'https://maps.google.com/maps?q={0:.4f},{1:.4f}&ll={0:.4f},{1:.4f}&z=16'
map_link = map_link_template.format(media.geo.lat, media.geo.long)
msg = map_link
@@ -353,7 +345,7 @@ class TelegramGateClient(TelegramClient):
def _process_info_msg(self, message, peer):
"""
Обрабатывает информационные сообщения в групповых чатах. Возвращает готовое для вывода сообщение.
Information messages.
:param message:
:param users:
:return:
@@ -411,14 +403,14 @@ class TelegramGateClient(TelegramClient):
def media_thread_downloader(self):
"""
Этот метод запускается в отдельном потоке и скачивает по очереди все медиа вложения из сообщений
Media downloader thread
:return:
"""
while True:
try:
if self._media_queue.empty(): # Нет медиа в очереди - спим
if self._media_queue.empty(): # queue is empty
time.sleep(0.1)
else: # Иначе скачиваем медиа
else: # queue is not empty
print('MTD ::: Queue is not empty. Downloading...')
media = self._media_queue.get()
file_path = self.xmpp_gate.config['media_store_path'] + media['file']