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-auth-httplib2 = "*"
google-auth-oauthlib = "*"
requests = "*"
[dev-packages]
pylint = "*"

View File

@ -27,9 +27,9 @@ Aviation weather TAF service access.
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
@ -51,6 +51,7 @@ Commands:
* !googlecal - Show next 10 events in calendar
* !googlecal today - Show today's events
* !googlecal add [calendar id] - Add new calendar to room
* !googlecal del [calendar id] - Delete calendar from room
* !googlecal calendars - List calendars in this room
## Bot setup

61
bot.py
View File

@ -8,10 +8,15 @@ import traceback
import importlib
import sys
import re
import requests
import json
import urllib.parse
from nio import (AsyncClient, RoomMessageText, RoomMessageUnknown, JoinError, InviteEvent)
class Bot:
appid = 'org.vranki.hemppa'
version = '1.0'
client = None
join_on_invite = False
modules = dict()
@ -25,11 +30,12 @@ class Bot:
}
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 = {
"msgtype": "m.text",
"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)
@ -40,6 +46,32 @@ class Bot:
print('Cannot find id for room', room.named_room_name(), ' - is the bot on it?')
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):
# Figure out the command
body = event.body
@ -104,6 +136,26 @@ class Bot:
traceback.print_exc(file=sys.stderr)
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):
self.client = AsyncClient(os.environ['MATRIX_SERVER'], os.environ['MATRIX_USER'])
self.client.access_token = os.getenv('MATRIX_ACCESS_TOKEN')
@ -114,7 +166,7 @@ class Bot:
print(f'Starting {len(self.modules)} modules..')
for modulename, moduleobject in self.modules.items():
print(modulename + '..')
print('Starting', modulename, '..')
if "matrix_start" in dir(moduleobject):
try:
moduleobject.matrix_start(bot)
@ -124,7 +176,7 @@ class Bot:
def stop(self):
print(f'Stopping {len(self.modules)} modules..')
for modulename, moduleobject in self.modules.items():
print(modulename + '..')
print('Stopping', modulename, '..')
if "matrix_stop" in dir(moduleobject):
try:
moduleobject.matrix_stop(bot)
@ -140,6 +192,7 @@ class Bot:
self.poll_task = asyncio.get_event_loop().create_task(self.poll_timer())
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.unknown_cb, RoomMessageUnknown)
if self.join_on_invite:

View File

@ -33,9 +33,7 @@ class MatrixModule:
if os.getenv("GCAL_CREDENTIALS"):
self.credentials_file = os.getenv("GCAL_CREDENTIALS")
self.service = None
self.report_time = 8
self.last_report_date = None
self.calendar_rooms = dict() # Contains rooms -> [calid, calid] ..
self.calendar_rooms = dict() # Contains room_id -> [calid, calid] ..
creds = None
@ -72,7 +70,7 @@ class MatrixModule:
return
args = event.body.split()
events = []
calendars = self.calendar_rooms.get(room) or []
calendars = self.calendar_rooms.get(room.room_id) or []
if len(args) == 2:
if args[1] == 'today':
@ -80,20 +78,38 @@ class MatrixModule:
print('Listing events in cal', calid)
events = events + self.list_today(calid)
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:
if args[1] == 'add':
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):
self.calendar_rooms[room].append(calid)
if self.calendar_rooms.get(room.room_id):
if calid not in self.calendar_rooms[room.room_id]:
self.calendar_rooms[room.room_id].append(calid)
else:
await bot.send_text(room, 'This google calendar already added in this room!')
return
else:
self.calendar_rooms[room] = [calid]
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')
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:
for calid in calendars:
print('Listing events in cal', calid)
@ -108,9 +124,9 @@ class MatrixModule:
async def send_events(self, bot, events, room):
for event in events:
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.parseDate(start)} {event['summary']} {event['htmlLink']}")
# await bot.send_html(room, self.parseDate(start) + " <a href=\"" + event['htmlLink'] + "\">" + event['summary'] + "</a>")
# await bot.send_text(room, f"{self.parse_date(start)} {event['summary']}")
# await bot.send_text(room, f"{self.parse_date(start)} {event['summary']} {event['htmlLink']}")
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):
startTime = datetime.datetime.utcnow()
@ -133,38 +149,17 @@ class MatrixModule:
events = events_result.get('items', [])
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):
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:
dt = datetime.datetime.strptime(start, '%Y-%m-%dT%H:%M:%S%z')
return dt.strftime("%d.%m %H:%M")