Google cal seems to work now!

This commit is contained in:
Ville Ranki 2019-12-10 18:05:40 +02:00
parent 5dc8c0b9c4
commit 9431d7a267
4 changed files with 98 additions and 48 deletions

View File

@ -9,6 +9,7 @@ geopy = "*"
google-api-python-client = "*" google-api-python-client = "*"
google-auth-httplib2 = "*" google-auth-httplib2 = "*"
google-auth-oauthlib = "*" google-auth-oauthlib = "*"
requests = "*"
[dev-packages] [dev-packages]
pylint = "*" pylint = "*"

View File

@ -27,9 +27,9 @@ Aviation weather TAF service access.
Prints bot uptime. Prints bot uptime.
### Google Calendar (WIP) ### Google Calendar
Displays changes and daily report of a google calendar to a room. This is a bit pain to set up, sorry. Can access a google calendar in a room. This is a bit pain to set up, sorry.
To set up, you'll need to generate credentials.json file - see https://console.developers.google.com/apis/credentials To set up, you'll need to generate credentials.json file - see https://console.developers.google.com/apis/credentials
@ -51,6 +51,7 @@ Commands:
* !googlecal - Show next 10 events in calendar * !googlecal - Show next 10 events in calendar
* !googlecal today - Show today's events * !googlecal today - Show today's events
* !googlecal add [calendar id] - Add new calendar to room * !googlecal add [calendar id] - Add new calendar to room
* !googlecal del [calendar id] - Delete calendar from room
* !googlecal calendars - List calendars in this room * !googlecal calendars - List calendars in this room
## Bot setup ## Bot setup

61
bot.py
View File

@ -8,10 +8,15 @@ import traceback
import importlib import importlib
import sys import sys
import re import re
import requests
import json
import urllib.parse
from nio import (AsyncClient, RoomMessageText, RoomMessageUnknown, JoinError, InviteEvent) from nio import (AsyncClient, RoomMessageText, RoomMessageUnknown, JoinError, InviteEvent)
class Bot: class Bot:
appid = 'org.vranki.hemppa'
version = '1.0'
client = None client = None
join_on_invite = False join_on_invite = False
modules = dict() modules = dict()
@ -25,11 +30,12 @@ class Bot:
} }
await self.client.room_send(self.get_room_id(room), 'm.room.message', msg) await self.client.room_send(self.get_room_id(room), 'm.room.message', msg)
async def send_html(self, room, html): async def send_html(self, room, html, plaintext):
msg = { msg = {
"msgtype": "m.text", "msgtype": "m.text",
"format": "org.matrix.custom.html", "format": "org.matrix.custom.html",
"formatted_body": html "formatted_body": html,
"body": plaintext
} }
await self.client.room_send(self.get_room_id(room), 'm.room.message', msg) await self.client.room_send(self.get_room_id(room), 'm.room.message', msg)
@ -40,6 +46,32 @@ class Bot:
print('Cannot find id for room', room.named_room_name(), ' - is the bot on it?') print('Cannot find id for room', room.named_room_name(), ' - is the bot on it?')
return None return None
def get_room_by_id(self, room_id):
return self.client.rooms[room_id]
def save_settings(self):
module_settings = dict()
for modulename, moduleobject in self.modules.items():
if "get_settings" in dir(moduleobject):
try:
module_settings[modulename] = moduleobject.get_settings()
except:
traceback.print_exc(file=sys.stderr)
print('Collected module settings:', module_settings)
data = { self.appid: self.version, 'module_settings': module_settings}
self.set_account_data(data)
def load_settings(self, data):
if not data.get('module_settings'):
return
for modulename, moduleobject in self.modules.items():
if data['module_settings'].get(modulename):
if "set_settings" in dir(moduleobject):
try:
moduleobject.set_settings(data['module_settings'][modulename])
except:
traceback.print_exc(file=sys.stderr)
async def message_cb(self, room, event): async def message_cb(self, room, event):
# Figure out the command # Figure out the command
body = event.body body = event.body
@ -104,6 +136,26 @@ class Bot:
traceback.print_exc(file=sys.stderr) traceback.print_exc(file=sys.stderr)
await asyncio.sleep(10) await asyncio.sleep(10)
def set_account_data(self, data):
userid = urllib.parse.quote(os.environ['MATRIX_USER'])
ad_url = f"{self.client.homeserver}/_matrix/client/r0/user/{userid}/account_data/{self.appid}?access_token={self.client.access_token}"
response = requests.put(ad_url, json.dumps(data))
if response.status_code != 200:
print('Setting account data failed:', response, response.json())
def get_account_data(self):
userid = urllib.parse.quote(os.environ['MATRIX_USER'])
ad_url = f"{self.client.homeserver}/_matrix/client/r0/user/{userid}/account_data/{self.appid}?access_token={self.client.access_token}"
response = requests.get(ad_url)
if response.status_code == 200:
return response.json()
elif response.status_code != 200:
print('Getting account data failed:', response, response.json())
def init(self): 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.client.access_token = os.getenv('MATRIX_ACCESS_TOKEN')
@ -114,7 +166,7 @@ class Bot:
print(f'Starting {len(self.modules)} modules..') print(f'Starting {len(self.modules)} modules..')
for modulename, moduleobject in self.modules.items(): for modulename, moduleobject in self.modules.items():
print(modulename + '..') print('Starting', modulename, '..')
if "matrix_start" in dir(moduleobject): if "matrix_start" in dir(moduleobject):
try: try:
moduleobject.matrix_start(bot) moduleobject.matrix_start(bot)
@ -124,7 +176,7 @@ class Bot:
def stop(self): def stop(self):
print(f'Stopping {len(self.modules)} modules..') print(f'Stopping {len(self.modules)} modules..')
for modulename, moduleobject in self.modules.items(): for modulename, moduleobject in self.modules.items():
print(modulename + '..') print('Stopping', modulename, '..')
if "matrix_stop" in dir(moduleobject): if "matrix_stop" in dir(moduleobject):
try: try:
moduleobject.matrix_stop(bot) moduleobject.matrix_stop(bot)
@ -140,6 +192,7 @@ class Bot:
self.poll_task = asyncio.get_event_loop().create_task(self.poll_timer()) self.poll_task = asyncio.get_event_loop().create_task(self.poll_timer())
if self.client.logged_in: if self.client.logged_in:
self.load_settings(self.get_account_data())
self.client.add_event_callback(self.message_cb, RoomMessageText) self.client.add_event_callback(self.message_cb, RoomMessageText)
self.client.add_event_callback(self.unknown_cb, RoomMessageUnknown) self.client.add_event_callback(self.unknown_cb, RoomMessageUnknown)
if self.join_on_invite: if self.join_on_invite:

View File

@ -33,9 +33,7 @@ class MatrixModule:
if os.getenv("GCAL_CREDENTIALS"): if os.getenv("GCAL_CREDENTIALS"):
self.credentials_file = os.getenv("GCAL_CREDENTIALS") self.credentials_file = os.getenv("GCAL_CREDENTIALS")
self.service = None self.service = None
self.report_time = 8 self.calendar_rooms = dict() # Contains room_id -> [calid, calid] ..
self.last_report_date = None
self.calendar_rooms = dict() # Contains rooms -> [calid, calid] ..
creds = None creds = None
@ -72,7 +70,7 @@ class MatrixModule:
return return
args = event.body.split() args = event.body.split()
events = [] events = []
calendars = self.calendar_rooms.get(room) or [] calendars = self.calendar_rooms.get(room.room_id) or []
if len(args) == 2: if len(args) == 2:
if args[1] == 'today': if args[1] == 'today':
@ -80,20 +78,38 @@ class MatrixModule:
print('Listing events in cal', calid) print('Listing events in cal', calid)
events = events + self.list_today(calid) events = events + self.list_today(calid)
if args[1] == 'calendars': if args[1] == 'calendars':
await bot.send_text(room, 'Calendars in this room: ' + str(self.calendar_rooms.get(room))) await bot.send_text(room, 'Calendars in this room: ' + str(self.calendar_rooms.get(room.room_id)))
elif len(args) == 3: elif len(args) == 3:
if args[1] == 'add': if args[1] == 'add':
calid = args[2] calid = args[2]
print(f'Adding calendar {calid} to room {room}') print(f'Adding calendar {calid} to room id {room.room_id}')
if self.calendar_rooms.get(room): if self.calendar_rooms.get(room.room_id):
self.calendar_rooms[room].append(calid) if calid not in self.calendar_rooms[room.room_id]:
self.calendar_rooms[room.room_id].append(calid)
else: else:
self.calendar_rooms[room] = [calid] await bot.send_text(room, 'This google calendar already added in this room!')
return
else:
self.calendar_rooms[room.room_id] = [calid]
print(f'Calendars now for this room {self.calendar_rooms[room]}') print(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
bot.save_settings()
await bot.send_text(room, 'Added new google calendar to this room') await bot.send_text(room, 'Added new google calendar to this room')
if args[1] == 'del':
calid = args[2]
print(f'Removing calendar {calid} from room id {room.room_id}')
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)}')
bot.save_settings()
await bot.send_text(room, 'Removed google calendar from this room')
else: else:
for calid in calendars: for calid in calendars:
print('Listing events in cal', calid) print('Listing events in cal', calid)
@ -108,9 +124,9 @@ class MatrixModule:
async def send_events(self, bot, events, room): async def send_events(self, bot, events, room):
for event in events: for event in events:
start = event['start'].get('dateTime', event['start'].get('date')) start = event['start'].get('dateTime', event['start'].get('date'))
await bot.send_text(room, f"{self.parseDate(start)} {event['summary']}") # await bot.send_text(room, f"{self.parse_date(start)} {event['summary']}")
# await bot.send_text(room, f"{self.parseDate(start)} {event['summary']} {event['htmlLink']}") # await bot.send_text(room, f"{self.parse_date(start)} {event['summary']} {event['htmlLink']}")
# await bot.send_html(room, self.parseDate(start) + " <a href=\"" + event['htmlLink'] + "\">" + event['summary'] + "</a>") await bot.send_html(room, f'{self.parse_date(start)} <a href="{event["htmlLink"]}">{event["summary"]}</a>', f'{self.parse_date(start)} {event["summary"]}')
def list_upcoming(self, calid): def list_upcoming(self, calid):
startTime = datetime.datetime.utcnow() startTime = datetime.datetime.utcnow()
@ -133,38 +149,17 @@ class MatrixModule:
events = events_result.get('items', []) events = events_result.get('items', [])
return events return events
async def matrix_poll(self, bot, pollcount):
if not self.service:
return
if pollcount % (6 * 5) == 0: # Poll every 5 min
pass # Not implemented yet
needs_send = False
today = datetime.datetime.now()
since_last = 999
# Bot's been started
if self.last_report_date:
since_last = (today - self.last_report_date).total_seconds() / 60 / 60
if since_last > 20 and today.hour >= self.report_time:
needs_send = True
if needs_send:
self.last_report_date = today
for room in self.calendar_rooms:
events = []
for calid in self.calendar_rooms.get(room):
events = events + self.list_today(calid)
await self.send_events(bot, events, room)
def help(self): def help(self):
return('Google calendar. Lists 10 next events by default. today = list today\'s events.') return('Google calendar. Lists 10 next events by default. today = list today\'s events.')
def parseDate(self, start): def get_settings(self):
return { 'calendar_rooms': self.calendar_rooms }
def set_settings(self, data):
if data.get('calendar_rooms'):
self.calendar_rooms = data['calendar_rooms']
def parse_date(self, start):
try: try:
dt = datetime.datetime.strptime(start, '%Y-%m-%dT%H:%M:%S%z') dt = datetime.datetime.strptime(start, '%Y-%m-%dT%H:%M:%S%z')
return dt.strftime("%d.%m %H:%M") return dt.strftime("%d.%m %H:%M")