commit
325e1e623c
|
@ -1,3 +1,9 @@
|
|||
# editors
|
||||
.vscode
|
||||
|
||||
# ignore Pipfile.lock
|
||||
Pipfile.lock
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
|
3
Pipfile
3
Pipfile
|
@ -16,6 +16,9 @@ igramscraper = "*"
|
|||
|
||||
[dev-packages]
|
||||
pylint = "*"
|
||||
pycodestyle = "*"
|
||||
flake8 = "*"
|
||||
autopep8 = "*"
|
||||
|
||||
[requires]
|
||||
python_version = "3.7"
|
||||
|
|
50
bot.py
50
bot.py
|
@ -1,25 +1,29 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
import json
|
||||
import glob
|
||||
import traceback
|
||||
import importlib
|
||||
import sys
|
||||
import re
|
||||
import requests
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import traceback
|
||||
import urllib.parse
|
||||
from nio import (AsyncClient, RoomMessageText, RoomMessageUnknown, JoinError, InviteEvent)
|
||||
|
||||
import requests
|
||||
from nio import AsyncClient, InviteEvent, JoinError, RoomMessageText
|
||||
|
||||
# Couple of custom exceptions
|
||||
|
||||
|
||||
class CommandRequiresAdmin(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class CommandRequiresOwner(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Bot:
|
||||
appid = 'org.vranki.hemppa'
|
||||
version = '1.1'
|
||||
|
@ -59,11 +63,12 @@ class Bot:
|
|||
if not self.is_owner(event):
|
||||
raise CommandRequiresOwner
|
||||
|
||||
# Returns true if event's sender is admin in the room event was sent in, or is bot owner
|
||||
# Returns true if event's sender is admin in the room event was sent in,
|
||||
# or is bot owner
|
||||
def is_admin(self, room, event):
|
||||
if self.is_owner(event):
|
||||
return True
|
||||
if not event.sender in room.power_levels.users:
|
||||
if event.sender not in room.power_levels.users:
|
||||
return False
|
||||
return room.power_levels.users[event.sender] >= 50
|
||||
|
||||
|
@ -77,7 +82,7 @@ class Bot:
|
|||
if "get_settings" in dir(moduleobject):
|
||||
try:
|
||||
module_settings[modulename] = moduleobject.get_settings()
|
||||
except:
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
data = {self.appid: self.version, 'module_settings': module_settings}
|
||||
self.set_account_data(data)
|
||||
|
@ -91,8 +96,9 @@ class Bot:
|
|||
if data['module_settings'].get(modulename):
|
||||
if "set_settings" in dir(moduleobject):
|
||||
try:
|
||||
moduleobject.set_settings(data['module_settings'][modulename])
|
||||
except:
|
||||
moduleobject.set_settings(
|
||||
data['module_settings'][modulename])
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
async def message_cb(self, room, event):
|
||||
|
@ -114,7 +120,7 @@ class Bot:
|
|||
await self.send_text(room, f'Sorry, you need admin power level in this room to run that command.')
|
||||
except CommandRequiresOwner:
|
||||
await self.send_text(room, f'Sorry, only bot owner can run that command.')
|
||||
except:
|
||||
except Exception:
|
||||
await self.send_text(room, f'Module {command} experienced difficulty: {sys.exc_info()[0]} - see log for details')
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
|
@ -129,7 +135,8 @@ class Bot:
|
|||
else:
|
||||
break
|
||||
else:
|
||||
print(f'Received invite event, but not joining as sender is not owner or bot not configured to join on invite. {event}')
|
||||
print(
|
||||
f'Received invite event, but not joining as sender is not owner or bot not configured to join on invite. {event}')
|
||||
|
||||
def load_module(self, modulename):
|
||||
try:
|
||||
|
@ -157,7 +164,7 @@ class Bot:
|
|||
if "matrix_poll" in dir(moduleobject):
|
||||
try:
|
||||
await moduleobject.matrix_poll(bot, self.pollcount)
|
||||
except:
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
await asyncio.sleep(10)
|
||||
|
||||
|
@ -178,11 +185,13 @@ class Bot:
|
|||
response = requests.get(ad_url)
|
||||
if response.status_code == 200:
|
||||
return response.json()
|
||||
print(f'Getting account data failed: {response} {response.json()} - this is normal if you have not saved any settings yet.')
|
||||
print(
|
||||
f'Getting account data failed: {response} {response.json()} - this is normal if you have not saved any settings yet.')
|
||||
return None
|
||||
|
||||
def init(self):
|
||||
self.client = AsyncClient(os.environ['MATRIX_SERVER'], os.environ['MATRIX_USER'])
|
||||
self.client = AsyncClient(
|
||||
os.environ['MATRIX_SERVER'], os.environ['MATRIX_USER'])
|
||||
self.client.access_token = os.getenv('MATRIX_ACCESS_TOKEN')
|
||||
self.join_on_invite = os.getenv("JOIN_ON_INVITE") is not None
|
||||
self.owners = os.environ['BOT_OWNERS'].split(',')
|
||||
|
@ -195,13 +204,14 @@ class Bot:
|
|||
if "matrix_stop" in dir(moduleobject):
|
||||
try:
|
||||
moduleobject.matrix_stop(bot)
|
||||
except:
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
async def run(self):
|
||||
if not self.client.access_token:
|
||||
await self.client.login(os.environ['MATRIX_PASSWORD'])
|
||||
print("Logged in with password, access token:", self.client.access_token)
|
||||
print("Logged in with password, access token:",
|
||||
self.client.access_token)
|
||||
|
||||
await self.client.sync()
|
||||
|
||||
|
@ -211,7 +221,7 @@ class Bot:
|
|||
if "matrix_start" in dir(moduleobject):
|
||||
try:
|
||||
moduleobject.matrix_start(bot)
|
||||
except:
|
||||
except Exception:
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
self.poll_task = asyncio.get_event_loop().create_task(self.poll_timer())
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import shlex
|
||||
from datetime import datetime
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
daily_commands = dict() # room_id -> command json
|
||||
last_hour = datetime.now().hour
|
||||
|
@ -16,7 +17,8 @@ class MatrixModule:
|
|||
dailycmd = args[2]
|
||||
if not self.daily_commands.get(room.room_id):
|
||||
self.daily_commands[room.room_id] = []
|
||||
self.daily_commands[room.room_id].append({ 'time': dailytime, 'command': dailycmd })
|
||||
self.daily_commands[room.room_id].append(
|
||||
{'time': dailytime, 'command': dailycmd})
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, 'Daily command added.')
|
||||
elif len(args) == 1:
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
from __future__ import print_function
|
||||
from datetime import datetime
|
||||
import datetime
|
||||
import pickle
|
||||
import os.path
|
||||
import time
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import pickle
|
||||
from datetime import datetime
|
||||
|
||||
from googleapiclient.discovery import build
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from google.auth.transport.requests import Request
|
||||
|
||||
|
||||
from google_auth_oauthlib.flow import InstalledAppFlow
|
||||
from googleapiclient.discovery import build
|
||||
|
||||
#
|
||||
# Google calendar notifications
|
||||
|
@ -20,6 +17,7 @@ from google.auth.transport.requests import Request
|
|||
# can be copied to another computer.
|
||||
#
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
def matrix_start(self, bot):
|
||||
self.bot = bot
|
||||
|
@ -45,7 +43,8 @@ class MatrixModule:
|
|||
else:
|
||||
flow = InstalledAppFlow.from_client_secrets_file(
|
||||
self.credentials_file, self.SCOPES)
|
||||
creds = flow.run_local_server(port=0) # urn:ietf:wg:oauth:2.0:oob
|
||||
# urn:ietf:wg:oauth:2.0:oob
|
||||
creds = flow.run_local_server(port=0)
|
||||
# Save the credentials for the next run
|
||||
with open('token.pickle', 'wb') as token:
|
||||
pickle.dump(creds, token)
|
||||
|
@ -54,14 +53,15 @@ class MatrixModule:
|
|||
self.service = build('calendar', 'v3', credentials=creds)
|
||||
|
||||
try:
|
||||
calendar_list = self.service.calendarList().list().execute()['items']
|
||||
print(f'Google calendar set up successfully with access to {len(calendar_list)} calendars:\n')
|
||||
calendar_list = self.service.calendarList().list().execute()[
|
||||
'items']
|
||||
print(
|
||||
f'Google calendar set up successfully with access to {len(calendar_list)} calendars:\n')
|
||||
for calendar in calendar_list:
|
||||
print(calendar['summary'] + ' - ' + calendar['id'])
|
||||
except:
|
||||
except Exception:
|
||||
print('Getting calendar list failed!')
|
||||
|
||||
|
||||
async def matrix_message(self, bot, room, event):
|
||||
if not self.service:
|
||||
await bot.send_text(room, 'Google calendar not set up for this bot.')
|
||||
|
@ -95,7 +95,8 @@ class MatrixModule:
|
|||
else:
|
||||
self.calendar_rooms[room.room_id] = [calid]
|
||||
|
||||
print(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
print(
|
||||
f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
|
||||
bot.save_settings()
|
||||
|
||||
|
@ -111,7 +112,8 @@ class MatrixModule:
|
|||
if self.calendar_rooms.get(room.room_id):
|
||||
self.calendar_rooms[room.room_id].remove(calid)
|
||||
|
||||
print(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
print(
|
||||
f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
|
||||
bot.save_settings()
|
||||
|
||||
|
@ -148,7 +150,8 @@ class MatrixModule:
|
|||
|
||||
def list_today(self, calid):
|
||||
startTime = datetime.datetime.utcnow()
|
||||
startTime = startTime.replace(hour = 0, minute = 0, second = 0, microsecond=0)
|
||||
startTime = startTime.replace(
|
||||
hour=0, minute=0, second=0, microsecond=0)
|
||||
endTime = startTime + datetime.timedelta(hours=24)
|
||||
now = startTime.isoformat() + 'Z'
|
||||
end = endTime.isoformat() + 'Z'
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
from igramscraper.instagram import Instagram
|
||||
from igramscraper.exception.instagram_not_found_exception import InstagramNotFoundException
|
||||
from datetime import datetime, timedelta
|
||||
from random import randrange
|
||||
|
||||
from igramscraper.exception.instagram_not_found_exception import \
|
||||
InstagramNotFoundException
|
||||
from igramscraper.instagram import Instagram
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
instagram = Instagram()
|
||||
|
||||
|
@ -27,7 +30,8 @@ class MatrixModule:
|
|||
try:
|
||||
await self.poll_account(bot, account, roomid, send_messages)
|
||||
except InstagramNotFoundException:
|
||||
print('ig error: there is ', account, ' account that does not exist - deleting from room')
|
||||
print('ig error: there is ', account,
|
||||
' account that does not exist - deleting from room')
|
||||
self.account_rooms[roomid].remove(account)
|
||||
bot.save_settings()
|
||||
|
||||
|
@ -77,7 +81,8 @@ class MatrixModule:
|
|||
else:
|
||||
self.account_rooms[room.room_id] = [account]
|
||||
|
||||
print(f'Accounts now for this room {self.account_rooms.get(room.room_id)}')
|
||||
print(
|
||||
f'Accounts now for this room {self.account_rooms.get(room.room_id)}')
|
||||
|
||||
try:
|
||||
await self.poll_account(bot, account, room.room_id, False)
|
||||
|
@ -91,12 +96,14 @@ class MatrixModule:
|
|||
bot.must_be_admin(room, event)
|
||||
|
||||
account = args[2]
|
||||
print(f'Removing account {account} from room id {room.room_id}')
|
||||
print(
|
||||
f'Removing account {account} from room id {room.room_id}')
|
||||
|
||||
if self.account_rooms.get(room.room_id):
|
||||
self.account_rooms[room.room_id].remove(account)
|
||||
|
||||
print(f'Accounts now for this room {self.account_rooms.get(room.room_id)}')
|
||||
print(
|
||||
f'Accounts now for this room {self.account_rooms.get(room.room_id)}')
|
||||
|
||||
bot.save_settings()
|
||||
await bot.send_text(room, 'Removed instagram account from this room')
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
from geopy.geocoders import Nominatim
|
||||
from nio import (RoomMessageUnknown)
|
||||
from nio import RoomMessageUnknown
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
bot = None
|
||||
|
||||
def matrix_start(self, bot):
|
||||
self.bot = bot
|
||||
bot.client.add_event_callback(self.unknown_cb, RoomMessageUnknown)
|
||||
|
@ -26,7 +28,8 @@ class MatrixModule:
|
|||
float(latlon[0])
|
||||
float(latlon[1])
|
||||
|
||||
osm_link = 'https://www.openstreetmap.org/?mlat=' + latlon[0] + "&mlon=" + latlon[1]
|
||||
osm_link = 'https://www.openstreetmap.org/?mlat=' + \
|
||||
latlon[0] + "&mlon=" + latlon[1]
|
||||
|
||||
plain = sender + ' 🚩 ' + osm_link
|
||||
html = f'{sender} 🚩 <a href={osm_link}>{location_text}</a>'
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import subprocess
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
async def matrix_message(self, bot, room, event):
|
||||
args = event.body.split()
|
||||
if len(args) == 2:
|
||||
icao = args[1]
|
||||
metar_url = "https://tgftp.nws.noaa.gov/data/observations/metar/stations/" + icao.upper() + ".TXT"
|
||||
metar_url = "https://tgftp.nws.noaa.gov/data/observations/metar/stations/" + \
|
||||
icao.upper() + ".TXT"
|
||||
response = urllib.request.urlopen(metar_url)
|
||||
lines = response.readlines()
|
||||
await bot.send_text(room, lines[1].decode("utf-8").strip())
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import os
|
||||
import urllib.request
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
async def matrix_message(self, bot, room, event):
|
||||
args = event.body.split()
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
from pyteamup import Calendar, Event
|
||||
from datetime import datetime
|
||||
import time
|
||||
import os
|
||||
from datetime import datetime
|
||||
|
||||
from pyteamup import Calendar
|
||||
|
||||
#
|
||||
# TeamUp calendar notifications
|
||||
#
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
api_key = None
|
||||
calendar_rooms = dict() # Roomid -> [calid, calid..]
|
||||
|
@ -24,10 +26,14 @@ class MatrixModule:
|
|||
calendar = self.calendars[calendarid]
|
||||
events = calendar.get_event_collection()
|
||||
for event in events:
|
||||
s = '<b>' + str(event.start_dt.day) + '.' + str(event.start_dt.month)
|
||||
s = '<b>' + str(event.start_dt.day) + \
|
||||
'.' + str(event.start_dt.month)
|
||||
if not event.all_day:
|
||||
s = s + ' ' + event.start_dt.strftime("%H:%M") + ' (' + str(event.duration) + ' min)'
|
||||
s = s + '</b> ' + event.title + " " + (event.notes or '')
|
||||
s = s + ' ' + \
|
||||
event.start_dt.strftime(
|
||||
"%H:%M") + ' (' + str(event.duration) + ' min)'
|
||||
s = s + '</b> ' + event.title + \
|
||||
" " + (event.notes or '')
|
||||
await bot.send_html(room, s, s)
|
||||
elif len(args) == 2:
|
||||
if args[1] == 'list':
|
||||
|
@ -51,7 +57,8 @@ class MatrixModule:
|
|||
else:
|
||||
self.calendar_rooms[room.room_id] = [calid]
|
||||
|
||||
print(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
print(
|
||||
f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
|
||||
bot.save_settings()
|
||||
self.setup_calendars()
|
||||
|
@ -65,7 +72,8 @@ class MatrixModule:
|
|||
if self.calendar_rooms.get(room.room_id):
|
||||
self.calendar_rooms[room.room_id].remove(calid)
|
||||
|
||||
print(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
print(
|
||||
f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
||||
|
||||
bot.save_settings()
|
||||
self.setup_calendars()
|
||||
|
@ -85,12 +93,12 @@ class MatrixModule:
|
|||
for roomid in self.calendar_rooms:
|
||||
calendars = self.calendar_rooms[roomid]
|
||||
for calendarid in calendars:
|
||||
events, timestamp = self.poll_server(self.calendars[calendarid])
|
||||
events, timestamp = self.poll_server(
|
||||
self.calendars[calendarid])
|
||||
self.calendars[calendarid].timestamp = timestamp
|
||||
for event in events:
|
||||
await bot.send_text(bot.get_room_by_id(roomid), 'Calendar: ' + self.eventToString(event))
|
||||
|
||||
|
||||
def poll_server(self, calendar):
|
||||
events, timestamp = calendar.get_changed_events(calendar.timestamp)
|
||||
return events, timestamp
|
||||
|
@ -111,9 +119,12 @@ class MatrixModule:
|
|||
if(event['delete_dt']):
|
||||
s = event['title'] + ' deleted.'
|
||||
else:
|
||||
s = event['title'] + " " + (event['notes'] or '') + ' ' + str(startdt.day) + '.' + str(startdt.month)
|
||||
s = event['title'] + " " + (event['notes'] or '') + \
|
||||
' ' + str(startdt.day) + '.' + str(startdt.month)
|
||||
if not event['all_day']:
|
||||
s = s + ' ' + startdt.strftime("%H:%M") + ' (' + str(event['duration']) + ' min)'
|
||||
s = s + ' ' + \
|
||||
startdt.strftime("%H:%M") + \
|
||||
' (' + str(event['duration']) + ' min)'
|
||||
return s
|
||||
|
||||
def setup_calendars(self):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import time
|
||||
|
||||
|
||||
class MatrixModule:
|
||||
def matrix_start(self, bot):
|
||||
self.starttime = time.time()
|
||||
|
|
Loading…
Reference in New Issue