2019-12-09 22:30:06 +02:00
|
|
|
from __future__ import print_function
|
2020-01-02 14:27:29 +02:00
|
|
|
|
2019-12-09 22:30:06 +02:00
|
|
|
import os
|
2020-01-02 14:27:29 +02:00
|
|
|
import os.path
|
|
|
|
import pickle
|
2020-01-05 22:12:35 +02:00
|
|
|
from datetime import datetime, timedelta
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
from google.auth.transport.requests import Request
|
2020-01-02 14:27:29 +02:00
|
|
|
from google_auth_oauthlib.flow import InstalledAppFlow
|
|
|
|
from googleapiclient.discovery import build
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
#
|
|
|
|
# Google calendar notifications
|
|
|
|
#
|
2020-01-02 11:54:31 +02:00
|
|
|
# Note: Provide a token.pickle file for the service.
|
|
|
|
# It's created on first run (run from console!) and
|
2019-12-09 22:30:06 +02:00
|
|
|
# can be copied to another computer.
|
|
|
|
#
|
2020-02-02 23:08:15 +02:00
|
|
|
from modules.common.module import BotModule
|
2019-12-09 22:30:06 +02:00
|
|
|
|
2020-01-02 14:27:29 +02:00
|
|
|
|
2020-02-02 23:08:15 +02:00
|
|
|
class MatrixModule(BotModule):
|
2020-02-06 21:56:53 +02:00
|
|
|
def __init__(self, name):
|
|
|
|
super().__init__(name)
|
2019-12-09 22:30:06 +02:00
|
|
|
self.credentials_file = "credentials.json"
|
2020-02-06 21:56:53 +02:00
|
|
|
self.SCOPES = ['https://www.googleapis.com/auth/calendar.readonly']
|
|
|
|
self.bot = None
|
2019-12-09 22:30:06 +02:00
|
|
|
self.service = None
|
2020-01-02 14:27:29 +02:00
|
|
|
self.calendar_rooms = dict() # Contains room_id -> [calid, calid] ..
|
2020-11-15 21:55:59 +02:00
|
|
|
self.enabled = False
|
2019-12-09 22:30:06 +02:00
|
|
|
|
2020-02-06 21:56:53 +02:00
|
|
|
def matrix_start(self, bot):
|
|
|
|
super().matrix_start(bot)
|
|
|
|
self.bot = bot
|
2019-12-09 22:30:06 +02:00
|
|
|
creds = None
|
|
|
|
|
2019-12-23 18:34:58 +02:00
|
|
|
if not os.path.exists(self.credentials_file) or os.path.getsize(self.credentials_file) == 0:
|
2020-01-02 14:27:29 +02:00
|
|
|
return # No-op if not set up
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
if os.path.exists('token.pickle'):
|
|
|
|
with open('token.pickle', 'rb') as token:
|
|
|
|
creds = pickle.load(token)
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info('Loaded existing pickle file')
|
2019-12-09 22:30:06 +02:00
|
|
|
# If there are no (valid) credentials available, let the user log in.
|
|
|
|
if not creds or not creds.valid:
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.warn('No credentials or credentials not valid!')
|
2019-12-09 22:30:06 +02:00
|
|
|
if creds and creds.expired and creds.refresh_token:
|
|
|
|
creds.refresh(Request())
|
|
|
|
else:
|
2020-02-06 21:56:53 +02:00
|
|
|
flow = InstalledAppFlow.from_client_secrets_file(self.credentials_file, self.SCOPES)
|
2020-01-02 14:27:29 +02:00
|
|
|
# urn:ietf:wg:oauth:2.0:oob
|
|
|
|
creds = flow.run_local_server(port=0)
|
2019-12-09 22:30:06 +02:00
|
|
|
# Save the credentials for the next run
|
|
|
|
with open('token.pickle', 'wb') as token:
|
|
|
|
pickle.dump(creds, token)
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info('Pickle saved')
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
self.service = build('calendar', 'v3', credentials=creds)
|
|
|
|
|
2019-12-10 23:43:08 +02:00
|
|
|
try:
|
2020-02-08 23:16:19 +02:00
|
|
|
calendar_list = self.service.calendarList().list().execute()['items']
|
|
|
|
self.logger.info(f'Google calendar set up successfully with access to {len(calendar_list)} calendars:\n')
|
2019-12-10 23:43:08 +02:00
|
|
|
for calendar in calendar_list:
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f"{calendar['summary']} - + {calendar['id']}")
|
2020-01-02 14:27:29 +02:00
|
|
|
except Exception:
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.error('Getting calendar list failed!')
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
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.')
|
|
|
|
return
|
|
|
|
args = event.body.split()
|
|
|
|
events = []
|
2019-12-10 18:05:40 +02:00
|
|
|
calendars = self.calendar_rooms.get(room.room_id) or []
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
if len(args) == 2:
|
|
|
|
if args[1] == 'today':
|
|
|
|
for calid in calendars:
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Listing events in cal {calid}')
|
2019-12-09 22:30:06 +02:00
|
|
|
events = events + self.list_today(calid)
|
2019-12-10 23:43:08 +02:00
|
|
|
if args[1] == 'list':
|
2019-12-10 18:05:40 +02:00
|
|
|
await bot.send_text(room, 'Calendars in this room: ' + str(self.calendar_rooms.get(room.room_id)))
|
2020-01-02 11:54:31 +02:00
|
|
|
return
|
|
|
|
|
2019-12-09 22:30:06 +02:00
|
|
|
elif len(args) == 3:
|
|
|
|
if args[1] == 'add':
|
2019-12-25 20:49:20 +02:00
|
|
|
bot.must_be_admin(room, event)
|
|
|
|
|
2019-12-09 22:30:06 +02:00
|
|
|
calid = args[2]
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Adding calendar {calid} to room id {room.room_id}')
|
2019-12-10 18:05:40 +02:00
|
|
|
|
|
|
|
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
|
2019-12-09 22:30:06 +02:00
|
|
|
else:
|
2019-12-10 18:05:40 +02:00
|
|
|
self.calendar_rooms[room.room_id] = [calid]
|
|
|
|
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
2019-12-09 22:30:06 +02:00
|
|
|
|
2019-12-10 18:05:40 +02:00
|
|
|
bot.save_settings()
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
await bot.send_text(room, 'Added new google calendar to this room')
|
2020-01-02 11:54:31 +02:00
|
|
|
return
|
|
|
|
|
2019-12-10 18:05:40 +02:00
|
|
|
if args[1] == 'del':
|
2019-12-25 20:49:20 +02:00
|
|
|
bot.must_be_admin(room, event)
|
|
|
|
|
2019-12-10 18:05:40 +02:00
|
|
|
calid = args[2]
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Removing calendar {calid} from room id {room.room_id}')
|
2019-12-10 18:05:40 +02:00
|
|
|
|
|
|
|
if self.calendar_rooms.get(room.room_id):
|
|
|
|
self.calendar_rooms[room.room_id].remove(calid)
|
|
|
|
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}')
|
2019-12-10 18:05:40 +02:00
|
|
|
|
|
|
|
bot.save_settings()
|
|
|
|
|
|
|
|
await bot.send_text(room, 'Removed google calendar from this room')
|
2020-01-02 11:54:31 +02:00
|
|
|
return
|
|
|
|
|
2019-12-09 22:30:06 +02:00
|
|
|
else:
|
|
|
|
for calid in calendars:
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Listing events in cal {calid}')
|
2019-12-09 22:30:06 +02:00
|
|
|
events = events + self.list_upcoming(calid)
|
|
|
|
|
2020-01-02 11:54:31 +02:00
|
|
|
if len(events) > 0:
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Found {len(events)} events')
|
2019-12-23 18:04:45 +02:00
|
|
|
await self.send_events(bot, events, room)
|
2020-01-02 11:54:31 +02:00
|
|
|
else:
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'No events found')
|
2020-01-02 11:54:31 +02:00
|
|
|
await bot.send_text(room, 'No events found, try again later :)')
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
async def send_events(self, bot, events, room):
|
|
|
|
for event in events:
|
|
|
|
start = event['start'].get('dateTime', event['start'].get('date'))
|
2020-02-02 23:08:15 +02:00
|
|
|
await bot.send_html(room, f'{self.parse_date(start)} <a href="{event["htmlLink"]}">{event["summary"]}</a>',
|
|
|
|
f'{self.parse_date(start)} {event["summary"]}')
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
def list_upcoming(self, calid):
|
2020-01-05 21:52:39 +02:00
|
|
|
startTime = datetime.utcnow()
|
2019-12-09 22:30:06 +02:00
|
|
|
now = startTime.isoformat() + 'Z'
|
|
|
|
events_result = self.service.events().list(calendarId=calid, timeMin=now,
|
2020-01-02 14:27:29 +02:00
|
|
|
maxResults=10, singleEvents=True,
|
|
|
|
orderBy='startTime').execute()
|
2019-12-09 22:30:06 +02:00
|
|
|
events = events_result.get('items', [])
|
|
|
|
return events
|
|
|
|
|
|
|
|
def list_today(self, calid):
|
2020-01-05 21:52:39 +02:00
|
|
|
startTime = datetime.utcnow()
|
2020-02-08 23:16:19 +02:00
|
|
|
startTime = startTime.replace(hour=0, minute=0, second=0, microsecond=0)
|
2020-01-05 22:12:35 +02:00
|
|
|
endTime = startTime + timedelta(hours=24)
|
2019-12-09 22:30:06 +02:00
|
|
|
now = startTime.isoformat() + 'Z'
|
|
|
|
end = endTime.isoformat() + 'Z'
|
2020-02-08 23:16:19 +02:00
|
|
|
self.logger.info(f'Looking for events between {now} {end}')
|
2019-12-09 22:30:06 +02:00
|
|
|
events_result = self.service.events().list(calendarId=calid, timeMin=now,
|
2020-01-02 14:27:29 +02:00
|
|
|
timeMax=end, maxResults=10, singleEvents=True,
|
|
|
|
orderBy='startTime').execute()
|
2019-12-23 18:04:45 +02:00
|
|
|
return events_result.get('items', [])
|
2019-12-09 22:30:06 +02:00
|
|
|
|
|
|
|
def help(self):
|
2020-02-08 23:16:19 +02:00
|
|
|
return 'Google calendar. Lists 10 next events by default. today = list today\'s events.'
|
2019-12-09 22:30:06 +02:00
|
|
|
|
2019-12-10 18:05:40 +02:00
|
|
|
def get_settings(self):
|
2020-02-06 01:19:45 +02:00
|
|
|
data = super().get_settings()
|
|
|
|
data['calendar_rooms'] = self.calendar_rooms
|
|
|
|
return data
|
2019-12-10 18:05:40 +02:00
|
|
|
|
|
|
|
def set_settings(self, data):
|
2020-02-06 01:19:45 +02:00
|
|
|
super().set_settings(data)
|
2019-12-10 18:05:40 +02:00
|
|
|
if data.get('calendar_rooms'):
|
|
|
|
self.calendar_rooms = data['calendar_rooms']
|
|
|
|
|
|
|
|
def parse_date(self, start):
|
2020-01-02 11:54:31 +02:00
|
|
|
try:
|
2020-01-05 22:12:35 +02:00
|
|
|
dt = datetime.strptime(start, '%Y-%m-%dT%H:%M:%S%z')
|
2019-12-09 22:30:06 +02:00
|
|
|
return dt.strftime("%d.%m %H:%M")
|
|
|
|
except ValueError:
|
2020-01-05 22:12:35 +02:00
|
|
|
dt = datetime.strptime(start, '%Y-%m-%d')
|
2019-12-09 22:30:06 +02:00
|
|
|
return dt.strftime("%d.%m")
|