Mastodon: Per-room accounts
This commit is contained in:
parent
28db0fedfe
commit
e372aadd73
23
README.md
23
README.md
|
@ -364,22 +364,33 @@ sends different messages and has not yet been reverse engineered.
|
|||
|
||||
### Mastodon
|
||||
|
||||
Send toots to Mastodon. You can login to Mastodon with the bot and toot with it. Login is
|
||||
personal - it's mapped to your Matrix ID. You can limit usage to bot owners only or make
|
||||
it public.
|
||||
Send toots to Mastodon. You can login to Mastodon with the bot and toot with it.
|
||||
|
||||
Normal login is personal - it's mapped to your Matrix ID.
|
||||
|
||||
Room login overrides personal login and allows room admins to toot to a specified account.
|
||||
|
||||
You can limit usage to bot owners only or make it public.
|
||||
|
||||
Note: You can subscribe to Mastodon users and hashtags with stock RSS feed integration.
|
||||
Mastodon generates the feeds automatically!
|
||||
|
||||
Commands:
|
||||
|
||||
* !md status - print status of mastodon module
|
||||
* !md login [instanceurl] [e-mail] [password] - log in your Matrix user to mastodon instance (do in private chat!)
|
||||
* !md toot [toot] - send a toot message
|
||||
* !md login [instanceurl] [e-mail] [password] - log in your Matrix user to Mastodon instance (do in private chat!)
|
||||
* !md toot [toot] - send a toot message. If said in a room with per-room login, user must be a admin in the room.
|
||||
* !md logout - log out the user from the bot
|
||||
* !md roomlogin [room] [instanceurl] [e-mail] [password] - same as login but assigns a per-room mastodon account (do in private chat!)
|
||||
* !md roomlogout - Say in a room that has per-room login. This will remove the login. Must be room admin.
|
||||
* !md setpublic - ANY user can use login command to login and use the Mastodon module (must be done as bot owner)
|
||||
* !md setprivate - Only bot owners can use Mastodon module (default) (must be done as bot owner)
|
||||
* !md clear - Clear all logins from the bot
|
||||
|
||||
Example of login command:
|
||||
Example commands:
|
||||
|
||||
* !md login https://my.instance/ me@email.invalid r00tm3
|
||||
* !md roomlogin #myroom:matrix.org https://my.instance/ me@email.invalid r00tm3
|
||||
|
||||
## Bot setup
|
||||
|
||||
|
|
13
bot.py
13
bot.py
|
@ -19,7 +19,7 @@ import datetime
|
|||
from importlib import reload
|
||||
|
||||
import requests
|
||||
from nio import AsyncClient, InviteEvent, JoinError, RoomMessageText, MatrixRoom, LoginError, RoomMemberEvent, RoomVisibility, RoomPreset, RoomCreateError
|
||||
from nio import AsyncClient, InviteEvent, JoinError, RoomMessageText, MatrixRoom, LoginError, RoomMemberEvent, RoomVisibility, RoomPreset, RoomCreateError, RoomResolveAliasResponse
|
||||
|
||||
|
||||
# Couple of custom exceptions
|
||||
|
@ -37,7 +37,7 @@ class Bot:
|
|||
|
||||
def __init__(self):
|
||||
self.appid = 'org.vranki.hemppa'
|
||||
self.version = '1.4'
|
||||
self.version = '1.5'
|
||||
self.client = None
|
||||
self.join_on_invite = False
|
||||
self.modules = dict()
|
||||
|
@ -144,6 +144,12 @@ class Bot:
|
|||
def get_room_by_id(self, room_id):
|
||||
return self.client.rooms[room_id]
|
||||
|
||||
async def get_room_by_alias(self, alias):
|
||||
rar = await self.client.room_resolve_alias(alias)
|
||||
if type(rar) is RoomResolveAliasResponse:
|
||||
return rar.room_id
|
||||
return None
|
||||
|
||||
# Throws exception if event sender is not a room admin
|
||||
def must_be_admin(self, room, event):
|
||||
if not self.is_admin(room, event):
|
||||
|
@ -197,7 +203,8 @@ class Bot:
|
|||
async def message_cb(self, room, event):
|
||||
# Ignore if asked to ignore
|
||||
if self.should_ignore_event(event):
|
||||
print('Ignoring this!')
|
||||
if self.debug:
|
||||
print('Ignoring event!')
|
||||
return
|
||||
|
||||
body = event.body
|
||||
|
|
|
@ -6,6 +6,7 @@ from modules.common.module import BotModule
|
|||
class MatrixModule(BotModule):
|
||||
apps = dict() # instance url <-> [app_id, app_secret]
|
||||
logins = dict() # mxid <-> [username, accesstoken, instanceurl]
|
||||
roomlogins = dict() # roomid <-> [username, accesstoken, instanceurl]
|
||||
public = False
|
||||
|
||||
async def matrix_message(self, bot, room, event):
|
||||
|
@ -16,16 +17,22 @@ class MatrixModule(BotModule):
|
|||
if not self.public:
|
||||
bot.must_be_owner(event)
|
||||
toot_body = " ".join(args[1:])
|
||||
if event.sender in self.logins.keys():
|
||||
accesstoken = None
|
||||
if room.room_id in self.roomlogins.keys():
|
||||
bot.must_be_admin(room, event)
|
||||
username = self.roomlogins[room.room_id][0]
|
||||
accesstoken = self.roomlogins[room.room_id][1]
|
||||
instanceurl = self.roomlogins[room.room_id][2]
|
||||
elif event.sender in self.logins.keys():
|
||||
username = self.logins[event.sender][0]
|
||||
accesstoken = self.logins[event.sender][1]
|
||||
instanceurl = self.logins[event.sender][2]
|
||||
if accesstoken:
|
||||
toottodon = Mastodon(
|
||||
access_token = accesstoken,
|
||||
api_base_url = instanceurl
|
||||
)
|
||||
tootdict = toottodon.toot(toot_body)
|
||||
print(tootdict)
|
||||
await bot.send_text(room, tootdict['url'])
|
||||
else:
|
||||
await bot.send_text(room, f'{event.sender} has not logged in yet with the bot. Please do so.')
|
||||
|
@ -39,27 +46,55 @@ class MatrixModule(BotModule):
|
|||
instanceurl = args[1]
|
||||
username = args[2]
|
||||
password = args[3]
|
||||
if not instanceurl in self.apps.keys():
|
||||
app = Mastodon.create_app(f'Hemppa The Bot - {bot.client.user}', api_base_url = instanceurl)
|
||||
self.apps[instanceurl] = [app[0], app[1]]
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, f'Registered Mastodon app on {instanceurl}')
|
||||
|
||||
mastodon = Mastodon(client_id = self.apps[instanceurl][0], client_secret = self.apps[instanceurl][1], api_base_url = instanceurl)
|
||||
access_token = mastodon.log_in(username, password)
|
||||
self.logins[mxid] = [username, access_token, instanceurl]
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, f'Logged into {instanceurl} as {username}')
|
||||
await self.register_app_if_necessary(bot, room, instanceurl)
|
||||
await self.login_to_account(bot, room, mxid, None, instanceurl, username, password)
|
||||
return
|
||||
if len(args) == 5:
|
||||
if args[0] == "roomlogin":
|
||||
if not self.public:
|
||||
bot.must_be_owner(event)
|
||||
roomalias = args[1]
|
||||
instanceurl = args[2]
|
||||
username = args[3]
|
||||
password = args[4]
|
||||
roomid = await bot.get_room_by_alias(roomalias)
|
||||
if roomid:
|
||||
await self.register_app_if_necessary(bot, room, instanceurl)
|
||||
await self.login_to_account(bot, room, None, roomid, instanceurl, username, password)
|
||||
else:
|
||||
await bot.send_text(room, f'Unknown room alias {roomalias} - invite bot to the room first.')
|
||||
return
|
||||
if len(args) == 1:
|
||||
if args[0] == "status":
|
||||
await bot.send_text(room, f'{len(self.logins)} users logged in, app registered on {len(self.apps)} instances, public use enabled: {self.public}')
|
||||
out = f'App registered on {len(self.apps)} instances, public use enabled: {self.public}\n'
|
||||
out = out + f'{len(self.logins)} users logged in:\n'
|
||||
for login in self.logins.keys():
|
||||
out = out + f' - {login} as {self.logins[login][0]} on {self.logins[login][2]}\n'
|
||||
out = out + f'{len(self.roomlogins)} per-room logins:\n'
|
||||
for roomlogin in self.roomlogins:
|
||||
out = out + f' - {roomlogin} as {self.roomlogins[roomlogin][0]} on {self.roomlogins[roomlogin][2]}\n'
|
||||
|
||||
await bot.send_text(room, out)
|
||||
if args[0] == "logout":
|
||||
if event.sender in self.logins.keys():
|
||||
# TODO: Is there a way to invalidate the access token with API?
|
||||
del self.logins[event.sender]
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, f'{event.sender} login data removed from the bot.')
|
||||
if args[0] == "roomlogout":
|
||||
bot.must_be_admin(room, event)
|
||||
if room.room_id in self.roomlogins.keys():
|
||||
del self.roomlogins[room.room_id]
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, f'Login data for this room removed from the bot.')
|
||||
else:
|
||||
await bot.send_text(room, f'No login found for room id {room.room_id}.')
|
||||
if args[0] == "clear":
|
||||
bot.must_be_owner(event)
|
||||
self.logins = dict()
|
||||
self.roomlogins = dict()
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, f'All Mastodon logins cleared')
|
||||
if args[0] == "setpublic":
|
||||
bot.must_be_owner(event)
|
||||
self.public = True
|
||||
|
@ -71,10 +106,31 @@ class MatrixModule(BotModule):
|
|||
bot.save_settings()
|
||||
await bot.send_text(room, f'Mastodon usage is now restricted to bot owners')
|
||||
|
||||
async def register_app_if_necessary(self, bot, room, instanceurl):
|
||||
if not instanceurl in self.apps.keys():
|
||||
app = Mastodon.create_app(f'Hemppa The Bot - {bot.client.user}', api_base_url = instanceurl)
|
||||
self.apps[instanceurl] = [app[0], app[1]]
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, f'Registered Mastodon app on {instanceurl}')
|
||||
|
||||
async def login_to_account(self, bot, room, mxid, roomid, instanceurl, username, password):
|
||||
mastodon = Mastodon(client_id = self.apps[instanceurl][0], client_secret = self.apps[instanceurl][1], api_base_url = instanceurl)
|
||||
access_token = mastodon.log_in(username, password)
|
||||
print('login_To_account', mxid, roomid)
|
||||
if mxid:
|
||||
self.logins[mxid] = [username, access_token, instanceurl]
|
||||
await bot.send_text(room, f'Logged Matrix user {mxid} into {instanceurl} as {username}')
|
||||
elif roomid:
|
||||
self.roomlogins[roomid] = [username, access_token, instanceurl]
|
||||
await bot.send_text(room, f'Set room {roomid} Mastodon user to {username} on {instanceurl}')
|
||||
|
||||
bot.save_settings()
|
||||
|
||||
def get_settings(self):
|
||||
data = super().get_settings()
|
||||
data['apps'] = self.apps
|
||||
data['logins'] = self.logins
|
||||
data['roomlogins'] = self.roomlogins
|
||||
data['public'] = self.public
|
||||
return data
|
||||
|
||||
|
@ -84,6 +140,8 @@ class MatrixModule(BotModule):
|
|||
self.apps = data["apps"]
|
||||
if data.get("logins"):
|
||||
self.logins = data["logins"]
|
||||
if data.get("roomlogins"):
|
||||
self.roomlogins = data["roomlogins"]
|
||||
if data.get("public"):
|
||||
self.public = data["public"]
|
||||
|
||||
|
|
Loading…
Reference in New Issue