Google cal seems to work now!
This commit is contained in:
parent
5dc8c0b9c4
commit
9431d7a267
1
Pipfile
1
Pipfile
|
@ -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 = "*"
|
||||||
|
|
|
@ -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
61
bot.py
|
@ -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:
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue