Compare commits

..

18 Commits

Author SHA1 Message Date
845af9008c Ignore asconfig.yaml 2017-10-30 10:39:47 -04:00
e5545ac9be Merge branch 'bot-owners' into develop 2017-10-30 10:39:05 -04:00
c25e34ebc3 Fix small config load issue with bot owners 2017-10-30 10:38:57 -04:00
532c3af254 Merge branch 'bot-owners' into develop 2017-10-30 10:32:19 -04:00
9ce2aa35ca Final if fix 2017-10-30 10:31:23 -04:00
b8e3eb6bf4 Merge branch 'bot-owners' into develop 2017-10-30 10:27:24 -04:00
b41badee88 Correct bot-owner if statements 2017-10-30 10:27:17 -04:00
7da7bc0baa Merge branch 'no-show-image-url' into develop 2017-10-30 10:19:14 -04:00
ac924fa420 Fix json formatting 2017-10-30 10:19:10 -04:00
55f0487391 Merge branch 'report-failed-join' into develop 2017-10-30 10:14:51 -04:00
8ad042dbb9 Merge no-show-image-url 2017-10-30 10:14:32 -04:00
fad2be42ef Merge branch 'bot-owners' into develop 2017-10-30 10:10:32 -04:00
f44c06daa0 Implement no-show-image-url 2017-10-30 10:09:45 -04:00
af7731f00e Document no-show-image-url 2017-10-30 10:09:35 -04:00
e20654668b Implement bot owners 2017-10-26 11:54:51 -04:00
30b5c6d3c6 Document bot-owners to allow some users to send messages without a prefix 2017-10-26 11:39:06 -04:00
7b8def4107 Log to console when a user cannot join a room 2017-10-26 11:26:51 -04:00
67647026ac Allow custom port for homeserver 2017-10-24 16:49:51 -04:00
4 changed files with 69 additions and 38 deletions

1
.gitignore vendored
View File

@ -152,4 +152,5 @@ Temporary Items
# Config # Config
config.json config.json
asconfig.yaml
database.db database.db

View File

@ -42,6 +42,8 @@ First, copy config.json.example to config.json. Then fill in the fields:
* `hosts.bare`: Just the (sub)domain of the server. * `hosts.bare`: Just the (sub)domain of the server.
* `user_id_format`: A Python `str.format`-style string to format user IDs as * `user_id_format`: A Python `str.format`-style string to format user IDs as
* `db_url`: A SQLAlchemy URL for the database. See the [SQLAlchemy docs](http://docs.sqlalchemy.org/en/latest/core/engines.html). * `db_url`: A SQLAlchemy URL for the database. See the [SQLAlchemy docs](http://docs.sqlalchemy.org/en/latest/core/engines.html).
* `bot_owners`: A list of matrix users the bot won't use a `<username>` prefix for. Can be left blank to print a prefix for all matrix users. Useful if you would like the bridge to replace Telegram for yourself, but also want to allow others to use the Matrix room.
* `print_url_with_image`: Set to `false` to disable sending the Matrix url of an image to Telegram.
**Synapse configuration** **Synapse configuration**

View File

@ -12,8 +12,16 @@
"bare": "DOMAIN.TLD" "bare": "DOMAIN.TLD"
}, },
"bot_owners": [
"@youruser:DOMAIN.TLD",
"@youruser:matrix.org",
"@youruser:example.com"
],
"user_id_format": "@telegram_{}:DOMAIN.TLD", "user_id_format": "@telegram_{}:DOMAIN.TLD",
"db_url": "sqlite:///database.db", "db_url": "sqlite:///database.db",
"print_url_with_image": true,
"as_port": 5000 "as_port": 5000
} }

View File

@ -12,7 +12,6 @@ from datetime import datetime
from time import time from time import time
from urllib.parse import unquote, quote, urlparse, parse_qs from urllib.parse import unquote, quote, urlparse, parse_qs
from io import BytesIO from io import BytesIO
import re
from PIL import Image from PIL import Image
from aiohttp import web, ClientSession from aiohttp import web, ClientSession
@ -39,6 +38,11 @@ try:
MATRIX_HOST_EXT = CONFIG['hosts']['external'] MATRIX_HOST_EXT = CONFIG['hosts']['external']
MATRIX_HOST_BARE = CONFIG['hosts']['bare'] MATRIX_HOST_BARE = CONFIG['hosts']['bare']
try:
MATRIX_BOT_OWNERS = CONFIG['bot_owners']
except KeyError:
MATRIX_BOT_OWNERS = {}
MATRIX_PREFIX = MATRIX_HOST + '_matrix/client/r0/' MATRIX_PREFIX = MATRIX_HOST + '_matrix/client/r0/'
MATRIX_MEDIA_PREFIX = MATRIX_HOST + '_matrix/media/r0/' MATRIX_MEDIA_PREFIX = MATRIX_HOST + '_matrix/media/r0/'
@ -46,6 +50,8 @@ try:
DATABASE_URL = CONFIG['db_url'] DATABASE_URL = CONFIG['db_url']
AS_PORT = CONFIG['as_port'] if 'as_port' in CONFIG else 5000 AS_PORT = CONFIG['as_port'] if 'as_port' in CONFIG else 5000
PRINT_URL_WITH_IMAGE = CONFIG['print_url_with_image'] if 'print_url_with_image' in CONFIG else True
except (OSError, IOError) as exception: except (OSError, IOError) as exception:
print('Error opening config file:') print('Error opening config file:')
print(exception) print(exception)
@ -83,26 +89,26 @@ def sanitize_html(string):
soup = BeautifulSoup(string, 'html.parser') soup = BeautifulSoup(string, 'html.parser')
for tag in soup.find_all(True): for tag in soup.find_all(True):
if tag.name == 'blockquote': if tag.name == 'blockquote':
tag.string = ('\n' + tag.text).replace('\n', '\n> ')[3:-3] tag.string = ('\n' + tag.text).replace('\n', '\n> ').rstrip('\n>')
if tag.name not in VALID_TAGS: if tag.name not in VALID_TAGS:
tag.hidden = True tag.hidden = True
return soup.renderContents().decode('utf-8') return soup.renderContents().decode('utf-8')
def format_matrix_msg(form, content): def format_matrix_msg(form, username, content):
""" """
Formats a matrix message for sending to Telegram Formats a matrix message for sending to Telegram
:param form: The format string of the message, where the first parameter :param form: The format string of the message, where the first parameter
is the username and the second one the message. is the username and the second one the message.
:param username: The username of the user.
:param content: The content to be sent. :param content: The content to be sent.
:return: The formatted string. :return: The formatted string.
""" """
if 'format' in content and content['format'] == 'org.matrix.custom.html': if 'format' in content and content['format'] == 'org.matrix.custom.html':
sanitized = re.sub("<a href=\\\"https://matrix.to/#/@telegram_([0-9]+):{}\\\">(.+?) \(Telegram\)</a>".format(MATRIX_HOST_BARE), "<a href=\"tg://user?id=\\1\">\\2</a>", content['formatted_body']) sanitized = sanitize_html(content['formatted_body'])
sanitized = sanitize_html(sanitized) return html.escape(form).format(username, sanitized), 'HTML'
return html.escape(form).format(sanitized), 'HTML'
else: else:
return form.format(html.escape(content['body'])), None return form.format(username, content['body']), None
async def download_matrix_file(url, filename): async def download_matrix_file(url, filename):
@ -184,7 +190,7 @@ async def matrix_transaction(request):
for alias in aliases: for alias in aliases:
print(alias) print(alias)
if alias.split('_')[0] != '#telegram' \ if alias.split('_')[0] != '#telegram' \
or alias.split(':')[-1] != MATRIX_HOST_BARE: or not alias.endswith(MATRIX_HOST_BARE):
continue continue
tg_id = alias.split('_')[1].split(':')[0] tg_id = alias.split('_')[1].split(':')[0]
@ -206,38 +212,44 @@ async def matrix_transaction(request):
if event['type'] == 'm.room.message': if event['type'] == 'm.room.message':
user_id = event['user_id'] user_id = event['user_id']
if matrix_is_telegram(user_id): if user_id in MATRIX_BOT_OWNERS:
continue displayname = ''
sender = db.session.query(db.MatrixUser)\
.filter_by(matrix_id=user_id).first()
if not sender:
response = await matrix_get('client', 'profile/{}/displayname'
.format(user_id), None)
try:
displayname = response['displayname']
except KeyError:
displayname = get_username(user_id)
sender = db.MatrixUser(user_id, displayname)
db.session.add(sender)
else: else:
displayname = sender.name or get_username(user_id) if matrix_is_telegram(user_id):
continue
sender = db.session.query(db.MatrixUser)\
.filter_by(matrix_id=user_id).first()
if not sender:
response = await matrix_get('client', 'profile/{}/displayname'
.format(user_id), None)
try:
displayname = response['displayname']
except KeyError:
displayname = get_username(user_id)
sender = db.MatrixUser(user_id, displayname)
db.session.add(sender)
else:
displayname = sender.name or get_username(user_id)
content = event['content'] content = event['content']
if 'msgtype' not in content: if 'msgtype' not in content:
continue continue
if content['msgtype'] == 'm.text': if content['msgtype'] == 'm.text':
msg, mode = format_matrix_msg('{}', content) prefix = '' if displayname == '' else '<' + displayname + '> '
response = await group.send_text("<b>{}:</b> {}".format(displayname, msg), parse_mode='HTML') msg, mode = format_matrix_msg('{}{}', prefix, content)
response = await group.send_text(msg, parse_mode=mode)
elif content['msgtype'] == 'm.notice': elif content['msgtype'] == 'm.notice':
msg, mode = format_matrix_msg('{}', content) prefix = '' if displayname == '' else '[' + displayname + '] '
response = await group.send_text("[{}] {}".format(displayname, msg), parse_mode=mode) msg, mode = format_matrix_msg('{}{}', prefix, content)
response = await group.send_text(msg, parse_mode=mode)
elif content['msgtype'] == 'm.emote': elif content['msgtype'] == 'm.emote':
msg, mode = format_matrix_msg('{}', content) prefix = '' if displayname == '' else '* ' + displayname + ' '
response = await group.send_text("* {} {}".format(displayname, msg), parse_mode=mode) msg, mode = format_matrix_msg('{}{}', prefix, content)
response = await group.send_text(msg, parse_mode=mode)
elif content['msgtype'] == 'm.image': elif content['msgtype'] == 'm.image':
try: try:
url = urlparse(content['url']) url = urlparse(content['url'])
@ -250,13 +262,18 @@ async def matrix_transaction(request):
# Download the file # Download the file
await download_matrix_file(url, content['body']) await download_matrix_file(url, content['body'])
with open('/tmp/{}'.format(content['body']), 'rb') as img_file: with open('/tmp/{}'.format(content['body']), 'rb') as img_file:
# Create the URL and shorten it url_str = ''
url_str = MATRIX_HOST_EXT + \ if PRINT_URL_WITH_IMAGE:
'_matrix/media/r0/download/{}{}' \ # Create the URL and shorten it
.format(url.netloc, quote(url.path)) url_str = MATRIX_HOST_EXT + \
url_str = await shorten_url(url_str) '_matrix/media/r0/download/{}{}' \
.format(url.netloc, quote(url.path))
url_str = await shorten_url(url_str)
url_str = ' (' + url_str + ')'
caption = '{} sent an image'.format(displayname) prefix = '' if displayname == '' else '<' + displayname + '> '
caption = '{}{}{}'.format(prefix,
content['body'], url_str)
response = await group.send_photo(img_file, caption=caption) response = await group.send_photo(img_file, caption=caption)
except: except:
pass pass
@ -449,7 +466,10 @@ async def register_join_matrix(chat, room_id, user_id):
await matrix_put('client', 'profile/{}/displayname'.format(user_id), await matrix_put('client', 'profile/{}/displayname'.format(user_id),
user_id, {'displayname': name}) user_id, {'displayname': name})
await matrix_post('client', 'join/{}'.format(room_id), user_id, {}) j = await matrix_post('client', 'join/{}'.format(room_id), user_id, {})
if 'errcode' in j and j['errcode'] == 'M_FORBIDDEN':
print("Error with <{}> joining room <{}>. This is likely because guests are not allowed to join the room."
.format(user_id, room_id))
async def update_matrix_displayname_avatar(tg_user): async def update_matrix_displayname_avatar(tg_user):
name = tg_user['first_name'] name = tg_user['first_name']