from __future__ import print_function import os import os.path import pickle from datetime import datetime, timedelta from google.auth.transport.requests import Request from google_auth_oauthlib.flow import InstalledAppFlow from googleapiclient.discovery import build # # Google calendar notifications # # Note: Provide a token.pickle file for the service. # It's created on first run (run from console!) and # can be copied to another computer. # from modules.common.module import BotModule class MatrixModule(BotModule): def matrix_start(self, bot): super().matrix_start(bot) self.bot = bot self.SCOPES = ['https://www.googleapis.com/auth/calendar.readonly'] self.credentials_file = "credentials.json" self.service = None self.calendar_rooms = dict() # Contains room_id -> [calid, calid] .. creds = None if not os.path.exists(self.credentials_file) or os.path.getsize(self.credentials_file) == 0: return # No-op if not set up if os.path.exists('token.pickle'): with open('token.pickle', 'rb') as token: creds = pickle.load(token) print('Loaded existing pickle file') # If there are no (valid) credentials available, let the user log in. if not creds or not creds.valid: print('No credentials or credentials not valid!') if creds and creds.expired and creds.refresh_token: creds.refresh(Request()) else: flow = InstalledAppFlow.from_client_secrets_file( self.credentials_file, self.SCOPES) # 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) print('Pickle saved') 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') for calendar in calendar_list: print(calendar['summary'] + ' - ' + calendar['id']) 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.') return args = event.body.split() events = [] calendars = self.calendar_rooms.get(room.room_id) or [] if len(args) == 2: if args[1] == 'today': for calid in calendars: print('Listing events in cal', calid) events = events + self.list_today(calid) if args[1] == 'list': await bot.send_text(room, 'Calendars in this room: ' + str(self.calendar_rooms.get(room.room_id))) return elif len(args) == 3: if args[1] == 'add': bot.must_be_admin(room, event) calid = args[2] print(f'Adding calendar {calid} to room id {room.room_id}') 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.room_id] = [calid] 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') return if args[1] == 'del': bot.must_be_admin(room, event) 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') return else: for calid in calendars: print('Listing events in cal', calid) events = events + self.list_upcoming(calid) if len(events) > 0: print(f'Found {len(events)} events') await self.send_events(bot, events, room) else: print(f'No events found') await bot.send_text(room, 'No events found, try again later :)') async def send_events(self, bot, events, room): for event in events: start = event['start'].get('dateTime', event['start'].get('date')) await bot.send_html(room, f'{self.parse_date(start)} {event["summary"]}', f'{self.parse_date(start)} {event["summary"]}') def list_upcoming(self, calid): startTime = datetime.utcnow() now = startTime.isoformat() + 'Z' events_result = self.service.events().list(calendarId=calid, timeMin=now, maxResults=10, singleEvents=True, orderBy='startTime').execute() events = events_result.get('items', []) return events def list_today(self, calid): startTime = datetime.utcnow() startTime = startTime.replace( hour=0, minute=0, second=0, microsecond=0) endTime = startTime + timedelta(hours=24) now = startTime.isoformat() + 'Z' end = endTime.isoformat() + 'Z' print('Looking for events between', now, end) events_result = self.service.events().list(calendarId=calid, timeMin=now, timeMax=end, maxResults=10, singleEvents=True, orderBy='startTime').execute() return events_result.get('items', []) def help(self): return ('Google calendar. Lists 10 next events by default. today = list today\'s events.') def get_settings(self): data = super().get_settings() data['calendar_rooms'] = self.calendar_rooms return data def set_settings(self, data): super().set_settings(data) if data.get('calendar_rooms'): self.calendar_rooms = data['calendar_rooms'] def parse_date(self, start): try: dt = datetime.strptime(start, '%Y-%m-%dT%H:%M:%S%z') return dt.strftime("%d.%m %H:%M") except ValueError: dt = datetime.strptime(start, '%Y-%m-%d') return dt.strftime("%d.%m")