[SVN] updated version to 0.3.0

[FIX] fixed sending subscription request from groups that are removed or from what you've been left and also fixed double auth request
[UPD] now using telethon version == 0.18
[UPD] code optimized and reworked
[UPD] status updates are moved to separate thread to use timer
[UPD] slightly changed status handling (now Available has "Online" status message, "Last seen recently" now is away, not XA, "Last seen ..." is now XA,  "Last seen long time ago" is now DND, chats are ffc)
[UPD] command "!del" removed and replaced with another, see above
[UPD] configuration options `xmpp_use_roster_exchange` and `xmpp_keep_online` was removed from configuration file
[UPD] [BREAK] database structure was changed; please, remove and re-create db.sqlite
[ADD] [BREAK] new options in config file: `logfile` (please, specify it!), and unneccessarry `tg_server_ip`, `tg_server_port`, `tg_server_dc`
[ADD] per-user configuration, parameters stored in database. configurable params:
      - use_roster_exchange: use XEP-0144 for roster import (default: false) (recommended: true, if your client supports that XEP)
      - keep_online: keep telegram session even if jabber goes offline (default: false) (recommended: true, if you wants to receive all events as offline messages when you will go online)
      - status_update_interval: interval (sec.) in what we will update all contact statuses to prevent presence spamming, because telegram sending status updates every fucking second (default: 60)
      To modify your personal config, please, send !configure to gateway
[ADD] added new commands to gateway:
      !configure (for configuration update)
      !add @contact (to find Telegram contact and try to start conversation; any format accepted (t.me link, @username or maybe phone, I don't know... )
      !join t.me/joinchat/secret (to join Telegram conference via invite link, https://t.me/joinchat/xxxxx accepted)
      !group name @contact (try to create normal group with @contact; you can add more later) [UNTESTED]
      !supergroup name (try to create supergroup) [UNTESTED]
      !channel name (try to create channel) [UNTESTED]
      !name first last (change telegram name)
      !username usernme (change telegram @username)
      !about some about text (change about text)
[ADD] added new commands to dialogs with normal users:
      !help
      !block (blacklists user)
      !unblock (unblacklists user)
[ADD] added new commands to group/channel dialogs:
      !help
      !leave (leave current group or supergroup)
      !invite (invite @user to current group/supergroup)
      !kick (kicks @user to/from group/supergroup)

... and also small fixes and improvements
This commit is contained in:
annelin
2018-07-01 09:42:35 +00:00
parent ea15b74348
commit 0f648e6bd4
6 changed files with 258 additions and 229 deletions

View File

@@ -4,6 +4,7 @@ CONFIG = {
'title': 'XMPP <-> Telegram Gate',
'debug': True,
'logfile': '/dev/null',
'jid': 'tlgrm.localhost',
'secret': 'secret',
@@ -15,6 +16,10 @@ CONFIG = {
'tg_api_id': '17349', # Telegram Desktop (GitHub)
'tg_api_hash': '344583e45741c457fe1862106095a5eb',
#'tg_server_ip': '149.154.167.50',
#'tg_server_port': 443,
#'tg_server_dc': 2,
'db_connect': 'db.sqlite',
'media_web_link_prefix': 'http://tlgrm.localhost/media/',

View File

@@ -44,7 +44,7 @@ logging.basicConfig(
level=logging.DEBUG if CONFIG['debug'] else logging.INFO,
format='%(asctime)s :: %(levelname)s:%(name)s :: %(message)s',
datefmt='%m/%d/%Y %I:%M:%S %p',
handlers=[logging.handlers.RotatingFileHandler(filename='gate.log'), logging.StreamHandler(sys.stdout)]
handlers=[logging.handlers.RotatingFileHandler(filename=CONFIG['logfile']), logging.StreamHandler(sys.stdout)]
)
# Создаем логгеры и перехватчики для STDOUT/STDERR
@@ -62,7 +62,7 @@ print('--- Telegram (MTProto) <---> XMPP Gateway ---')
print('----------------------------------------------------------------------')
print()
print('Starting...')
print('Gate build: rev{}'.format(xmpp_tg.__version__))
print('Gate version: {}'.format(xmpp_tg.__version__))
print('Process pid: {}'.format(os.getpid()))
print('Using Telethon v{} and SleekXMPP v{}'.format(telethon.TelegramClient.__version__, sleekxmpp.__version__))
print()

View File

@@ -1,3 +1,3 @@
from xmpp_tg.xmpp import XMPPTelegram
__version__ = 15
__version__ = '0.3.0'

View File

@@ -42,30 +42,32 @@ class TelegramGateClient(TelegramClient):
self.xmpp_gate = xmpp_gate
self.jid = jid
self.phone = phone
self.user_options = {'nl_after_info': True, 'status_update_interval': 60}
self._media_queue = queue.Queue()
self._media_thread = threading.Thread(name='MediaDownloaderThread', target=self.media_thread_downloader)
self._media_thread.start()
self._status_updates = dict()
self._status_update_thread = threading.Thread(name = 'StatusUpdateThread', target = self.status_updater_thread)
self._groups_users = dict()
self._message_cache_users = dict()
self._message_cache_groups = dict()
self._message_cache_supergroups = dict()
self._status_last = dict()
self._del_pts = 0
def xmpp_update_handler(self, obj):
print('new update for ' + self.jid)
print(type(obj), obj.__dict__)
# link to self-user #
print("We have received update for <%s>" % self.jid)
print(obj)
# we have received some updates, so we're logined and can get <me> object and start mtd / upd threads #
if not self.me:
me = self.get_me()
self.me = InputPeerUser(me.id, me.access_hash)
self._media_thread.start()
self._status_update_thread.start()
'''
Боты
@@ -80,7 +82,7 @@ class TelegramGateClient(TelegramClient):
# Здесь будет очень длинный пиздец ^__^
nl = '\n' if self.user_options['nl_after_info'] else ''
nl = '\n'
try:
@@ -95,16 +97,8 @@ class TelegramGateClient(TelegramClient):
# message from normal group #
if type(obj) in [UpdateShortChatMessage] and not obj.out:
fwd_from = self._process_forward_msg(obj) if obj.fwd_from else '' # process forward messages
nickname = ''
# get sender information from chat info
if obj.from_id not in self._groups_users:
chat_info = self.invoke(GetFullChatRequest(obj.chat_id))
for usr in chat_info.users:
self._groups_users[usr.id] = usr
nickname = display_tg_name(self._groups_users[obj.from_id].first_name, self._groups_users[obj.from_id].last_name)
usr = self._get_user_information(obj.from_id)
nickname = display_tg_name(usr)
# send message
self.gate_send_message(mfrom='g' + str(obj.chat_id), mbody ='[MSG {}] [User: {}] {}{}'.format(obj.id, nickname, fwd_from, obj.message) )
@@ -159,7 +153,7 @@ class TelegramGateClient(TelegramClient):
# get sender information from chat info #
if not is_user and not obj.message.post:
usr = self._get_user_information(obj.message.from_id)
nickname = display_tg_name(usr.first_name, usr.last_name)
nickname = display_tg_name(usr)
msg = '[User: {}] {}'.format(nickname, msg)
@@ -183,34 +177,24 @@ class TelegramGateClient(TelegramClient):
# Status Updates #
if type(obj) is UpdateUserStatus:
# save last update time #
if (obj.user_id in self._status_last) and ( (time.time() - self._status_last[obj.user_id]['time'] < self.user_options['status_update_interval']) or self._status_last[obj.user_id]['status'] == obj.status ):
return
self._status_last[obj.user_id] = {'status': obj.status, 'time': time.time()}
# process status update #
if type(obj.status) is UserStatusOnline:
self.xmpp_gate.send_presence( pto=self.jid, pfrom='u'+str(obj.user_id)+'@'+self.xmpp_gate.config['jid'])
self._status_updates[str(obj.user_id)] = { 'status': None, 'message': 'Online' }
elif type(obj.status) is UserStatusOffline:
self.xmpp_gate.send_presence( pto=self.jid, pfrom='u'+str(obj.user_id)+'@'+self.xmpp_gate.config['jid'], ptype='xa', pstatus=obj.status.was_online.strftime('Last seen at %H:%M %d/%m/%Y') )
self._status_updates[str(obj.user_id)] = { 'status': 'xa', 'message': obj.status.was_online.strftime('Last seen at %H:%M %d/%m/%Y') }
elif type(obj.status) is UserStatusRecently:
self.xmpp_gate.send_presence( pto=self.jid, pfrom='u' + str(obj.user_id) + '@' + self.xmpp_gate.config['jid'], pstatus='Last seen recently' )
self._status_updates[str(obj.user_id)] = { 'status': 'away', 'message': 'Last seen recently' }
else:
print(type(obj.status))
print(obj.update.status.__dict__)
pass
except Exception:
print('Exception occurs!')
print(traceback.format_exc())
print(' ')
def gate_send_message(self, mfrom, mbody):
tg_from = int(mfrom[1:])
if not tg_from in self.xmpp_gate.tg_dialogs[self.jid]['users'] and not tg_from in self.xmpp_gate.tg_dialogs[self.jid]['groups'] and not tg_from in self.xmpp_gate.tg_dialogs[self.jid]['supergroups']:
print('Re-init dialog list...')
if not tg_from in self.xmpp_gate.tg_dialogs[self.jid]['users'] and not tg_from in self.xmpp_gate.tg_dialogs[self.jid]['groups'] and not tg_from in self.xmpp_gate.tg_dialogs[self.jid]['supergroups']: # new contact appeared
self.xmpp_gate.tg_process_dialogs( self.jid )
self.xmpp_gate.send_message( mto=self.jid, mfrom=mfrom + '@' + self.xmpp_gate.config['jid'], mtype='chat', mbody=mbody)
@@ -279,7 +263,7 @@ class TelegramGateClient(TelegramClient):
if message.fwd_from.from_id: # От пользователя
usr = self._get_user_information(message.fwd_from.from_id)
fwd_from = display_tg_name(usr.first_name, usr.last_name)
fwd_from = display_tg_name(usr)
if message.fwd_from.channel_id: # От канала
fwd_from = 'Channel {}'.format(message.fwd_from.channel_id)
@@ -296,7 +280,6 @@ class TelegramGateClient(TelegramClient):
:return:
"""
msg = ''
# print(var_dump(media))
if type(media) is MessageMediaDocument: # Документ или замаскированная сущность
attributes = media.document.attributes
@@ -378,7 +361,7 @@ class TelegramGateClient(TelegramClient):
msg = ''
usr = self._get_user_information(message.from_id)
nickname = display_tg_name(usr.first_name, usr.last_name)
nickname = display_tg_name(usr)
# supergroup created #
if type(message.action) is MessageActionChannelCreate:
@@ -393,19 +376,19 @@ class TelegramGateClient(TelegramClient):
added_users = []
for user_id in message.action.users:
usr = self._get_user_information(user_id)
added_users.append(display_tg_name(usr.first_name, usr.last_name))
added_users.append(display_tg_name(usr))
msg = 'User [{}] has just invited [{}]'.format(nickname, ','.join(added_users))
# user exit #
elif type(message.action) is MessageActionChatDeleteUser:
usr = self._get_user_information(message.action.user_id)
msg = 'User [{}] has just left the room'.format(display_tg_name(usr.first_name, usr.last_name))
msg = 'User [{}] has just left the room'.format(display_tg_name(usr))
# user joined #
elif type(message.action) is MessageActionChatJoinedByLink:
usr = self._get_user_information(message.action.user_id)
msg = 'User [{}] joined the room'.format(display_tg_name(usr.first_name, usr.last_name))
msg = 'User [{}] joined the room'.format(display_tg_name(usr))
# chat name modified #
elif type(message.action) is MessageActionChatEditTitle:
@@ -418,7 +401,7 @@ class TelegramGateClient(TelegramClient):
if len(message_req.messages) > 0:
pinned_message = message_req.messages[0].message
pinned_from = self._get_user_information(message_req.messages[0].from_id)
msg = 'User [{}] pinned message: [{}]: {}'.format(nickname, display_tg_name(pinned_from.first_name, pinned_from.last_name), pinned_message)
msg = 'User [{}] pinned message: [{}]: {}'.format(nickname, display_tg_name(pinned_from), pinned_message)
# group converted to supergroup
elif type(message.action) in [MessageActionChatMigrateTo, MessageActionChannelMigrateFrom]:
@@ -446,3 +429,16 @@ class TelegramGateClient(TelegramClient):
print('MTD ::: Media downloaded')
except Exception:
print(traceback.format_exc())
def status_updater_thread(self):
while True:
try:
if len(self._status_updates) > 0:
for uid, status in self._status_updates.items():
self.xmpp_gate.send_presence( pto=self.jid, pfrom='u'+str(uid)+'@'+self.xmpp_gate.config['jid'], pshow = status['status'], pstatus = status['message'] )
except Exception:
print(traceback.format_exc())
self._status_updates = dict()
time.sleep( self.xmpp_gate.accounts[self.jid]['status_update_interval'])

View File

@@ -6,23 +6,35 @@ import types
from datetime import datetime
def display_tg_name(first_name, last_name):
if first_name and last_name:
return '{} {}'.format(first_name, last_name)
elif first_name:
return first_name
elif last_name:
return last_name
def display_tg_name(peer):
if hasattr(peer,'title') and hasattr(peer,'broadcast') and peer.broadcast: # channel
return '[C] ' + peer.title
elif hasattr(peer,'title') and hasattr(peer,'broadcast') and not peer.broadcast: # supergroup
return '[SG] ' + peer.title
elif hasattr(peer,'title'): # normal group
return '[G] ' + peer.title
elif peer.first_name and peer.last_name: # user with first and last name
return '{} {}'.format(peer.first_name, peer.last_name)
elif peer.first_name: # user with firstname only
return peer.first_name
elif peer.last_name: # user with lastname only
return peer.last_name
elif peer.username: # user with username only
return peer.username
else: # no match, unknown contact
return '[Unknown]'
def get_contact_jid(peer, gatejid):
if peer.id and hasattr(peer,'title') and hasattr(peer, 'broadcast') and peer.broadcast: # channel
return 'c' + str(peer.id) + '@' + gatejid
elif peer.id and hasattr(peer,'title') and hasattr(peer,'broadcast') and not peer.broadcast: # supergroup
return 's' + str(peer.id) + '@' + gatejid
elif peer.id and hasattr(peer,'title'): # normal group
return 'g' + str(peer.id) + '@' + gatejid
elif peer.id and not peer.bot: # it is... user?
return 'u' + str(peer.id) + '@' + gatejid
else:
return '[No name]'
def make_gate_jid():
pass
def parse_gate_jid():
pass
return None
def var_dump(obj, depth=7, l=""):

View File

@@ -1,28 +1,30 @@
import sqlite3
import re
import sys
import time
import sleekxmpp
from sleekxmpp.componentxmpp import ComponentXMPP
import xml.etree.ElementTree as ET
from telethon.tl.functions.messages import GetDialogsRequest, SendMessageRequest
from telethon.tl.functions.account import UpdateStatusRequest, GetAuthorizationsRequest
from telethon.tl.functions.contacts import DeleteContactRequest
from telethon.tl.functions.channels import JoinChannelRequest, LeaveChannelRequest
from telethon.tl.functions.messages import GetDialogsRequest, SendMessageRequest, ImportChatInviteRequest, GetFullChatRequest, AddChatUserRequest, DeleteChatUserRequest, CreateChatRequest
from telethon.tl.functions.account import UpdateStatusRequest, GetAuthorizationsRequest, UpdateProfileRequest, UpdateUsernameRequest
from telethon.tl.functions.contacts import DeleteContactRequest, BlockRequest, UnblockRequest
from telethon.tl.functions.channels import JoinChannelRequest, LeaveChannelRequest, InviteToChannelRequest, EditBannedRequest, CreateChannelRequest
from telethon.tl.types import InputPeerEmpty, InputPeerUser, InputPeerChat, InputPeerChannel
from telethon.tl.types import User, Chat, Channel
from telethon.tl.types import PeerChannel, PeerChat, PeerUser, Chat, ChatForbidden, Channel, ChannelForbidden
from telethon.tl.types import UserStatusOnline, UserStatusRecently, UserStatusOffline
from telethon.tl.types import Updates, UpdateShortSentMessage, UpdateMessageID
from telethon.tl.types import ChannelBannedRights
from telethon.tl.types.messages import Dialogs, DialogsSlice
from telethon.helpers import generate_random_long
from telethon.errors import SessionPasswordNeededError
from xmpp_tg.mtproto import TelegramGateClient
from xmpp_tg.utils import var_dump, display_tg_name
from xmpp_tg.utils import var_dump, display_tg_name, get_contact_jid
import xmpp_tg.monkey # Патчим баги в библиотеках
class XMPPTelegram(ComponentXMPP):
@@ -40,9 +42,10 @@ class XMPPTelegram(ComponentXMPP):
config_dict['port'])
self.auto_authorize = True
self.auto_subscribe = True
# self.auto_subscribe = True
self.config = config_dict
self.accounts = dict() # personal configuration per JID
self.tg_connections = dict()
self.tg_phones = dict()
self.tg_dialogs = dict()
@@ -92,7 +95,7 @@ class XMPPTelegram(ComponentXMPP):
"""
users = self.db_connection.execute("SELECT * FROM accounts").fetchall()
for usr in users:
print('Sending presence...')
self.accounts[usr['jid']] = usr
self.send_presence(pto=usr['jid'], pfrom=self.boundjid.bare, ptype='probe')
def message(self, iq):
@@ -111,15 +114,13 @@ class XMPPTelegram(ComponentXMPP):
else: # Пишут в Telegram
if jid in self.tg_connections and self.tg_connections[jid].is_user_authorized():
if iq['body'].startswith('!'): # Команда из чата
print('command received')
if iq['to'].bare.startswith('u'):
self.process_chat_user_command(iq)
elif iq['to'].bare.startswith('g') or iq['to'].bare.startswith('s'):
elif iq['to'].bare.startswith('g') or iq['to'].bare.startswith('s') or iq['to'].bare.startswith('c'):
self.process_chat_group_command(iq)
else:
self.gate_reply_message(iq, 'Error.')
else: # Обычное сообщение
print('sent message')
tg_id = int(iq['to'].node[1:])
tg_peer = None
msg = iq['body']
@@ -140,8 +141,6 @@ class XMPPTelegram(ComponentXMPP):
elif iq['to'].bare.startswith('s') or iq['to'].bare.startswith('c'): # Супергруппа
tg_peer = InputPeerChannel(tg_id, self.tg_dialogs[jid]['supergroups'][tg_id].access_hash)
print(tg_peer)
if tg_peer:
# Отправляем сообщение и получаем новый апдейт
result = self.tg_connections[jid].invoke(
@@ -163,7 +162,6 @@ class XMPPTelegram(ComponentXMPP):
# mbody='[Your MID:{}]'.format(msg_id))
def event_presence_unsub(self, presence):
print('defense')
return
def event_presence(self, presence):
@@ -177,11 +175,11 @@ class XMPPTelegram(ComponentXMPP):
# handle "online" to transport:
if ptype == 'available' and presence['to'].bare == self.boundjid.bare:
self.handle_online(presence)
self.handle_online(presence, False) # handle online
elif ptype == 'subscribe':
self.send_presence(pto=presence['from'].bare, pfrom=presence['to'].bare, ptype='subscribed')
elif ptype == 'subscribed':
self.send_presence(pto=presence['from'].bare, pfrom=presence['to'].bare, ptype='subscribee')
pass
elif ptype == 'unsubscribe':
pass
elif ptype == 'unsubscribed':
@@ -194,7 +192,7 @@ class XMPPTelegram(ComponentXMPP):
# self.send_presence(pto=presence['from'], pfrom=presence['to'])
pass
def handle_online(self, event):
def handle_online(self, event, sync_roster = True):
"""
Обработчик события online. Подключается к Telegram при наличии авторизации.
:param event:
@@ -218,7 +216,7 @@ class XMPPTelegram(ComponentXMPP):
self.tg_connections[jid].invoke(UpdateStatusRequest(offline=False))
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='online', pstatus='connected')
self.tg_process_dialogs(jid)
self.tg_process_dialogs(jid, sync_roster) # do not sync roster if we already have connection!
def handle_offline(self, event):
@@ -230,7 +228,7 @@ class XMPPTelegram(ComponentXMPP):
jid = event['from'].bare
# keep telegram online ?
if self.config['xmpp_keep_online']:
if self.accounts[jid]['keep_online']:
return
if jid in self.tg_connections:
@@ -258,39 +256,38 @@ class XMPPTelegram(ComponentXMPP):
jid = iq['from'].bare
if parsed[0] == '!help':
self.gate_reply_message(iq, 'Available command:\n\n'
self.gate_reply_message(iq, '=== Available gateway commands ===:\n\n'
'!help - Displays this text\n'
'!login +123456789 - Initiates Telegram session\n'
'!code 12345 - Entering one-time code during auth\n'
#'!password abc123 - Entering password during two-factor auth\n'
'!list_sessions - List all created sessions at Telegram servers\n'
'!password abc123 - Entering password during two-factor auth\n'
'!configure - Configure transport settings\n'
#'!list_sessions - List all created sessions at Telegram servers\n'
#'!delete_session 123 - Delete session\n'
'!logout - Deletes current Telegram session at gate\n'
'!reload_dialogs - Reloads dialogs list from Telegram\n\n'
'!add - Find and add Telegram contact. Any formats accepted (nickname or t.me link)\n\n'
'!del - Removes Telegram contact or leaves a chat. Any formats accepted (nickname or t.me link) \n\n'
#'!create_group - Initiates group creation\n'
#'!create_channel - Initiates channel creation\n\n'
#'!change_name first last - Changes your name in Telegram\n'
#'!change_username username - Changes your @username in Telegram\n'
# '!blocked_users_list\n'
# '!blocked_users_add\n'
# '!blocked_users_remove\n'
# '!last_seen_privacy_status\n'
# '!last_seen_privacy_set\n'
# '!last_seen_privacy_never_add\n'
# '!last_seen_privacy_never_remove\n'
# '!last_seen_privacy_always_add\n'
# '!last_seen_privacy_always_remove\n'
# '!group_invite_settings_status\n'
# '!group_invite_settings_set\n'
# '!group_invite_settings_add\n'
# '!group_invite_settings_remove\n'
# '!group_invite_settings_add\n'
# '!group_invite_settings_remove\n'
# '!account_selfdestruct_setting_status\n'
# '!account_selfdestruct_setting_set\n'
'!join - Join Telegram conference via invite link \n\n'
'!group GroupName @InviteContact - Create a normal group\n'
'!supergroup SupergroupName - Create a supergroup\n'
'!channel ChannelName - Create a channel\n\n'
'!name first last - Change your name in Telegram\n'
'!about text - Change about text in Telegram\n'
'!username - Changes your @username in Telegram\n'
)
elif parsed[0] == '!configure':
config_exclude = ['jid', 'tg_phone']
if len(parsed) > 2 and parsed[1] not in config_exclude:
self.db_connection.execute("update accounts set {} = ? where jid = ?".format(parsed[1]), (parsed[2],jid,) )
self.accounts[jid] = self.db_connection.execute("SELECT * FROM accounts where jid = ?", (jid,) ).fetchone()
message = "=== Your current configuration ===\n\n"
for param, value in self.accounts[jid].items():
message = message + "<%s>: %s" % (param, value) + "\n"
message = message + "\nTo modify some option, please, send !configure param value"
self.gate_reply_message(iq, message)
elif parsed[0] == '!login': # --------------------------------------------------
self.gate_reply_message(iq, 'Please wait...')
self.spawn_tg_client(jid, parsed[1])
@@ -320,8 +317,10 @@ class XMPPTelegram(ComponentXMPP):
if self.tg_connections[jid].is_user_authorized():
self.send_presence(pto=jid, pfrom=self.boundjid.bare, ptype='online', pstatus='connected')
self.gate_reply_message(iq, 'Authentication successful. Initiating Telegram...')
self.db_connection.execute("INSERT INTO accounts(jid, tg_phone) VALUES(?, ?)", (jid, self.tg_phones[jid],))
self.accounts[jid] = self.db_connection.execute("SELECT * FROM accounts where jid = ?", (jid,) ).fetchone()
self.init_tg(jid)
self.db_connection.execute("INSERT INTO accounts VALUES(?, ?)", (jid, self.tg_phones[jid],))
else:
self.gate_reply_message(iq, 'Authentication failed.')
else:
@@ -332,7 +331,6 @@ class XMPPTelegram(ComponentXMPP):
return
sessions = self.tg_connections[jid].invoke(GetAuthorizationsRequest())
print(sessions.__dict__)
elif parsed[0] == '!reload_dialogs':
if not self.tg_connections[jid].is_user_authorized():
self.gate_reply_message(iq, 'Error.')
@@ -357,87 +355,123 @@ class XMPPTelegram(ComponentXMPP):
self.tg_process_dialogs(jid)
elif parsed[0] == '!del': # add user
result = self.tg_connections[jid].get_entity(parsed[1])
if type(result) == User:
tg_peer = InputPeerUser( result.id, result.access_hash )
req = self.tg_connections[jid].invoke( DeleteContactRequest(tg_peer) )
print(req)
prefix = 'u'
elif type(result) == Channel:
tg_peer = InputPeerChannel( result.id, result.access_hash )
self.tg_connections[jid].invoke(LeaveChannelRequest( InputPeerChannel(result.id, result.access_hash) ) )
prefix = 'c' if result.broadcast else 's'
else:
self.gate_reply_message(iq, 'Sorry, nothing found.')
return
elif parsed[0] == '!join': # join chat by link
link = parsed[1].split('/') # https://t.me/joinchat/HrCmckx_SkMbSGFLhXCvSg
self.tg_connections[jid].invoke(ImportChatInviteRequest(link[4]))
time.sleep(1)
self.tg_process_dialogs(jid)
presence_from = prefix + str(result.id) + '@' + self.boundjid.bare
self.send_presence(pto=jid, pfrom=presence_from, ptype='unavailable')
self.send_presence(pto=jid, pfrom=presence_from, ptype='unsubscribed')
self.send_presence(pto=jid, pfrom=presence_from, ptype='unsubscribe')
elif parsed[0] == '!group' and len(parsed) >= 3: # create new group
# group name? #
groupname = parsed[1]
# group users? #
groupuser = self.tg_connections[jid].get_entity(parsed[2])
# we re ready to make group
self.tg_connections[jid].invoke(CreateChatRequest([groupusers], groupname))
self.tg_process_dialogs(jid)
elif parsed[0] == '!channel' and len(parsed) >= 2: # create new channel
groupname = parsed[1]
self.tg_connections[jid].invoke(CreateChannelRequest(groupname, groupname, broadcast = True))
self.tg_process_dialogs(jid)
elif parsed[0] == '!supergroup' and len(parsed) >= 2: # create new channel
groupname = parsed[1]
self.tg_connections[jid].invoke(CreateChannelRequest(groupname, groupname, megagroup = True))
self.tg_process_dialogs(jid)
elif parsed[0] == '!username' and len(parsed) >= 2: # create new channel
username = parsed[1]
self.tg_connections[jid].invoke(UpdateUsernameRequest(username))
elif parsed[0] == '!name' and len(parsed) >= 2: # create new channel
firstname = parsed[1]
lastname = parsed[2] if len(parsed) > 2 else None
self.tg_connections[jid].invoke(UpdateProfileRequest(first_name = firstname, last_name = lastname))
elif parsed[0] == '!about' and len(parsed) >= 2: # create new channel
about = iq['body'][7:]
self.tg_connections[jid].invoke(UpdateProfileRequest(about = about))
else: # --------------------------------------------------
self.gate_reply_message(iq, 'Unknown command. Try !help for list all commands.')
def process_chat_user_command(self, iq):
parsed = []
parsed = iq['body'].split(' ')
jid = iq['from'].bare
if parsed[0] == '!help':
self.gate_reply_message(iq, '=== Available dialog commands ===:\n\n'
'!help - Displays this text\n'
'!block - Blacklists current user\n'
'!unblock - Unblacklists current user\n'
)
elif parsed[0] == '!block':
tg_id = int(iq['to'].node[1:])
nickname = display_tg_name(self.tg_dialogs[jid]['users'][tg_id])
self.tg_connections[jid].invoke(BlockRequest( InputPeerUser(tg_id, self.tg_dialogs[jid]['users'][tg_id].access_hash) ) )
self.gate_reply_message(iq, 'User %s blacklisted!' % nickname)
elif parsed[0] == '!unblock':
tg_id = int(iq['to'].node[1:])
nickname = display_tg_name(self.tg_dialogs[jid]['users'][tg_id])
self.tg_connections[jid].invoke(UnblockRequest( InputPeerUser(tg_id, self.tg_dialogs[jid]['users'][tg_id].access_hash) ) )
self.gate_reply_message(iq, 'User %s unblacklisted!' % nickname)
if parsed[0] == '!search':
pass
elif parsed[0] == '!get_history':
pass
elif parsed[0] == '!forward_messages':
pass
elif parsed[0] == '!delete_messages':
pass
elif parsed[0] == '!block_status':
pass
elif parsed[0] == '!block_set':
pass
elif parsed[0] == '!block_unser':
pass
elif parsed[0] == '!clear_history':
pass
elif parsed[0] == '!delete_conversation':
pass
elif parsed[0] == '!help':
pass
def process_chat_group_command(self, iq):
parsed = []
parsed = iq['body'].split(' ')
jid = iq['from'].bare
if parsed[0] == '!help':
self.gate_reply_message(iq, '=== Available chat commands ===:\n\n'
'!help - Displays this text\n'
'!leave - Leaves current group or supergroup\n'
'!invite - Invites user to group\n'
'!kick - Kicks user to group\n'
)
elif parsed[0] == '!leave':
tg_id = int(iq['to'].node[1:])
if tg_id in self.tg_dialogs[jid]['supergroups']:
self.tg_connections[jid].invoke(LeaveChannelRequest( InputPeerChannel(tg_id, self.tg_dialogs[jid]['supergroups'][tg_id].access_hash) ) )
c_jid = get_contact_jid(self.tg_dialogs[jid]['supergroups'][tg_id], self.boundjid.bare)
self.send_presence(pto = jid, pfrom = c_jid, ptype = 'unavailable')
self.send_presence(pto = jid, pfrom = c_jid, ptype = 'unsubscribed')
self.send_presence(pto = jid, pfrom = c_jid, ptype = 'unsubscribe')
if tg_id in self.tg_dialogs[jid]['groups']:
self.tg_connections[jid].invoke( DeleteChatUserRequest(tg_id, self.tg_connections[jid].me) )
c_jid = get_contact_jid(self.tg_dialogs[jid]['groups'][tg_id], self.boundjid.bare)
self.send_presence(pto = jid, pfrom = c_jid, ptype = 'unavailable')
self.send_presence(pto = jid, pfrom = c_jid, ptype = 'unsubscribed')
self.send_presence(pto = jid, pfrom = c_jid, ptype = 'unsubscribe')
elif parsed[0] == '!invite':
tg_id = int(iq['to'].node[1:])
if tg_id in self.tg_dialogs[jid]['supergroups']:
invited_user = self.tg_connections[jid].get_entity(parsed[1])
if type(invited_user) == User:
self.tg_connections[jid].invoke(EditBannedRequest( InputPeerChannel(tg_id, self.tg_dialogs[jid]['supergroups'][tg_id].access_hash), invited_user, ChannelBannedRights(until_date=None,view_messages=False) ) )
self.tg_connections[jid].invoke(InviteToChannelRequest( InputPeerChannel(tg_id, self.tg_dialogs[jid]['supergroups'][tg_id].access_hash), [invited_user] ) )
if tg_id in self.tg_dialogs[jid]['groups']:
invited_user = self.tg_connections[jid].get_entity(parsed[1])
if type(invited_user) == User:
self.tg_connections[jid].invoke( AddChatUserRequest(tg_id, invited_user, 0) )
elif parsed[0] == '!kick':
tg_id = int(iq['to'].node[1:])
if tg_id in self.tg_dialogs[jid]['supergroups']:
kicked_user = self.tg_connections[jid].get_entity(parsed[1])
if type(invited_user) == User:
self.tg_connections[jid].invoke(EditBannedRequest( InputPeerChannel(tg_id, self.tg_dialogs[jid]['supergroups'][tg_id].access_hash), kicked_user, ChannelBannedRights(until_date=None,view_messages=True) ) )
if tg_id in self.tg_dialogs[jid]['groups']:
kicked_user = self.tg_connections[jid].get_entity(parsed[1])
if type(invited_user) == User:
self.tg_connections[jid].invoke( DeleteChatUserRequest(tg_id, kicked_user) )
if parsed[0] == '!search':
pass
elif parsed[0] == '!get_history':
pass
elif parsed[0] == '!forward_messages':
pass
elif parsed[0] == '!delete_messages':
pass
elif parsed[0] == '!pin_message':
pass
elif parsed[0] == '!unpin_message':
pass
elif parsed[0] == '!leave_group':
pass
elif parsed[0] == '!add_members':
pass
elif parsed[0] == '!bans_list':
pass
elif parsed[0] == '!ban_user':
pass
elif parsed[0] == '!unban_user':
pass
elif parsed[0] == '!restrict_user':
pass
elif parsed[0] == '!unrestrict_user':
pass
elif parsed[0] == '!get_recent_actions':
pass
elif parsed[0] == '!get_recent_actions':
pass
def spawn_tg_client(self, jid, phone):
"""
@@ -446,8 +480,9 @@ class XMPPTelegram(ComponentXMPP):
:param phone:
:return:
"""
client = TelegramGateClient('a_'+phone, int(self.config['tg_api_id']), self.config['tg_api_hash'],
self, jid, phone)
client = TelegramGateClient('a_'+phone, int(self.config['tg_api_id']), self.config['tg_api_hash'], self, jid, phone)
if 'tg_server_ip' in self.config and 'tg_server_dc' in self.config and 'tg_server_port' in self.config:
client.session.set_dc(self.config['tg_server_dc'], self.config['tg_server_ip'], self.config['tg_server_port'])
client.connect()
self.tg_connections[jid] = client
@@ -498,9 +533,8 @@ class XMPPTelegram(ComponentXMPP):
self.send(presence)
def tg_process_dialogs(self, jid):
def tg_process_dialogs(self, jid, sync_roster = True):
print('! -- Process Dialogs -- !')
# Инициализируем словари для диалогов
self.tg_dialogs[jid] = dict()
self.tg_dialogs[jid]['raw'] = list()
@@ -534,15 +568,15 @@ class XMPPTelegram(ComponentXMPP):
if type(dlg.peer) is PeerUser:
usr = self.tg_dialogs[jid]['users'][dlg.peer.user_id]
vcard = self.plugin['xep_0054'].make_vcard()
u_jid = 'u' + str(usr.id) + '@' + self.boundjid.bare
u_jid = get_contact_jid(usr, self.boundjid.bare)
if usr.deleted:
rostername = "Deleted Account"
vcard['FN'] = 'Deleted account'
vcard['DESC'] = 'This user no longer exists in Telegram'
else:
rostername = display_tg_name(usr.first_name, usr.last_name)
vcard['FN'] = display_tg_name(usr.first_name, usr.last_name)
rostername = display_tg_name(usr)
vcard['FN'] = display_tg_name(usr)
if usr.first_name:
vcard['N']['GIVEN'] = usr.first_name
if usr.last_name:
@@ -559,62 +593,50 @@ class XMPPTelegram(ComponentXMPP):
self.plugin['xep_0054'].publish_vcard(jid=u_jid, vcard=vcard)
self.plugin['xep_0172'].publish_nick(nick=vcard['FN'], ifrom=u_jid)
# self.send_presence(pto=jid, pfrom=u_jid, ptype='subscribe')
self.contact_list[jid][u_jid] = rostername
if usr.bot:
self.send_presence(pto=jid, pfrom=u_jid, pstatus='Bot')
self.send_presence(pto=jid, pfrom=u_jid, pshow = 'chat', pstatus='Bot')
else:
if type(usr.status) is UserStatusOnline:
self.send_presence(pto=jid, pfrom=u_jid)
self.send_presence(pto=jid, pfrom=u_jid, pstatus = 'Online' )
elif type(usr.status) is UserStatusRecently:
self.send_presence(pto=jid, pfrom=u_jid, pshow='away', pstatus='Last seen recently')
elif type(usr.status) is UserStatusOffline:
self.send_presence(
pto=jid,
pfrom=u_jid,
ptype='xa',
pstatus=usr.status.was_online.strftime('Last seen at %H:%M %d/%m/%Y')
)
self.send_presence(pto=jid, pfrom=u_jid, pshow='xa', pstatus=usr.status.was_online.strftime('Last seen at %H:%M %d/%m/%Y') )
else:
self.send_presence(pto=jid, pfrom=u_jid, ptype='unavailable',
pstatus='Last seen a long time ago')
self.send_presence(pto=jid, pfrom=u_jid, pshow='dnd', pstatus='Last seen a long time ago')
if type(dlg.peer) in [PeerChat, PeerChannel]:
g_type = ''
cht = None
if type(dlg.peer) is PeerChat: # Старая группа
cht = self.tg_dialogs[jid]['groups'][dlg.peer.chat_id]
c_jid = 'g' + str(cht.id) + '@' + self.boundjid.bare
g_type = 'G'
cht = self.tg_connections[jid].invoke(GetFullChatRequest(dlg.peer.chat_id))
cht = cht.chats[0]
if cht.deactivated or cht.left:
cht = None
elif type(dlg.peer) is PeerChannel: # Супергруппа
cht = self.tg_dialogs[jid]['supergroups'][dlg.peer.channel_id]
if cht.broadcast:
g_type = 'C'
c_jid = 'c' + str(cht.id) + '@' + self.boundjid.bare
else:
g_type = 'SG'
c_jid = 's' + str(cht.id) + '@' + self.boundjid.bare
rostername = '[{}] {}'.format(g_type, cht.title)
if cht and cht.id:
rostername = display_tg_name(cht)
c_jid = get_contact_jid(cht, self.boundjid.bare)
vcard = self.plugin['xep_0054'].make_vcard()
vcard['FN'] = '[{}] {}'.format(g_type, cht.title)
vcard['NICKNAME'] = vcard['FN']
vcard['JABBERID'] = c_jid
self.plugin['xep_0054'].publish_vcard(jid=c_jid, vcard=vcard)
self.plugin['xep_0172'].publish_nick(nick=vcard['FN'], ifrom=c_jid)
vcard = self.plugin['xep_0054'].make_vcard()
vcard['FN'] = rostername
vcard['NICKNAME'] = rostername
vcard['JABBERID'] = c_jid
self.plugin['xep_0054'].publish_vcard(jid=c_jid, vcard=vcard)
self.plugin['xep_0172'].publish_nick(nick=vcard['FN'], ifrom=c_jid)
self.contact_list[jid][c_jid] = rostername
#self.send_presence(pto=jid, pfrom=c_jid, ptype='subscribe')
self.send_presence(pto=jid, pfrom=c_jid)
self.contact_list[jid][c_jid] = rostername
self.send_presence(pto=jid, pfrom=c_jid, pshow = 'chat', pstatus = cht.title)
if len(dlgs.dialogs) == 0: # Если все диалоги получены - прерываем цикл
if self.config['xmpp_use_roster_exchange']:
if sync_roster and 'use_roster_exchange' in self.accounts[jid] and self.accounts[jid]['use_roster_exchange'] == 'true':
self.roster_exchange(jid, self.contact_list[jid])
else:
elif sync_roster:
self.roster_fill(jid, self.contact_list[jid])
break
else: # Иначе строим оффсеты
@@ -659,12 +681,6 @@ class XMPPTelegram(ComponentXMPP):
conn = sqlite3.connect(self.config['db_connect'], isolation_level=None, check_same_thread=False)
conn.row_factory = dict_factory
conn.execute("CREATE TABLE IF NOT EXISTS accounts("
"jid VARCHAR(255),"
"tg_phone VARCHAR(25)"
")")
# conn.execute("CREATE TABLE IF NOT EXISTS roster("
# "")
conn.execute("CREATE TABLE IF NOT EXISTS accounts(jid VARCHAR(255), tg_phone VARCHAR(25), use_roster_exchange BOOLEAN default false, keep_online BOOLEAN default false, status_update_interval INTEGER default 60)")
return conn