From a7b15c6a1eac5a8ab10c30ab7637869618e2de17 Mon Sep 17 00:00:00 2001 From: Tatu Wikman Date: Thu, 2 Jan 2020 13:18:35 +0200 Subject: [PATCH 1/5] ignore vscode --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index b6e4761..216bda2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ +# editors +.vscode + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] From 5d8f02513fa392fdca5d01cde2f20f6af1bdac4b Mon Sep 17 00:00:00 2001 From: Tatu Wikman Date: Thu, 2 Jan 2020 13:19:35 +0200 Subject: [PATCH 2/5] ignore pipfile.lock, although this should probably be added... --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 216bda2..ef64859 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # editors .vscode +# ignore Pipfile.lock +Pipfile.lock + # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] From 2033ced1239954b8a009dd86f6efa088c28eb349 Mon Sep 17 00:00:00 2001 From: Tatu Wikman Date: Thu, 2 Jan 2020 13:20:06 +0200 Subject: [PATCH 3/5] flake, autopep etc --- Pipfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Pipfile b/Pipfile index c42a3df..0f5460c 100644 --- a/Pipfile +++ b/Pipfile @@ -16,6 +16,9 @@ igramscraper = "*" [dev-packages] pylint = "*" +pycodestyle = "*" +flake8 = "*" +autopep8 = "*" [requires] python_version = "3.7" From 2b51544f07c6ee3deea6c48744ab1d8ca4117b39 Mon Sep 17 00:00:00 2001 From: Tatu Wikman Date: Thu, 2 Jan 2020 14:27:29 +0200 Subject: [PATCH 4/5] flake8 passes --- bot.py | 58 ++++++++++++++++++++++++++------------------ modules/cron.py | 10 +++++--- modules/googlecal.py | 51 ++++++++++++++++++++------------------ modules/ig.py | 25 ++++++++++++------- modules/loc.py | 9 ++++--- modules/metar.py | 6 ++--- modules/taf.py | 4 +-- modules/teamup.py | 47 +++++++++++++++++++++-------------- modules/uptime.py | 1 + 9 files changed, 124 insertions(+), 87 deletions(-) diff --git a/bot.py b/bot.py index 462f322..c9d3459 100755 --- a/bot.py +++ b/bot.py @@ -1,25 +1,29 @@ #!/usr/bin/env python3 import asyncio -import os -import json import glob -import traceback import importlib -import sys -import re -import requests import json +import os +import re +import sys +import traceback import urllib.parse -from nio import (AsyncClient, RoomMessageText, RoomMessageUnknown, JoinError, InviteEvent) + +import requests +from nio import AsyncClient, InviteEvent, JoinError, RoomMessageText # Couple of custom exceptions + + class CommandRequiresAdmin(Exception): pass + class CommandRequiresOwner(Exception): pass + class Bot: appid = 'org.vranki.hemppa' version = '1.1' @@ -59,11 +63,12 @@ class Bot: if not self.is_owner(event): raise CommandRequiresOwner - # Returns true if event's sender is admin in the room event was sent in, or is bot owner + # Returns true if event's sender is admin in the room event was sent in, + # or is bot owner def is_admin(self, room, event): if self.is_owner(event): return True - if not event.sender in room.power_levels.users: + if event.sender not in room.power_levels.users: return False return room.power_levels.users[event.sender] >= 50 @@ -77,9 +82,9 @@ class Bot: if "get_settings" in dir(moduleobject): try: module_settings[modulename] = moduleobject.get_settings() - except: + except Exception: traceback.print_exc(file=sys.stderr) - data = { self.appid: self.version, 'module_settings': module_settings} + data = {self.appid: self.version, 'module_settings': module_settings} self.set_account_data(data) def load_settings(self, data): @@ -91,8 +96,9 @@ class Bot: if data['module_settings'].get(modulename): if "set_settings" in dir(moduleobject): try: - moduleobject.set_settings(data['module_settings'][modulename]) - except: + moduleobject.set_settings( + data['module_settings'][modulename]) + except Exception: traceback.print_exc(file=sys.stderr) async def message_cb(self, room, event): @@ -114,7 +120,7 @@ class Bot: await self.send_text(room, f'Sorry, you need admin power level in this room to run that command.') except CommandRequiresOwner: await self.send_text(room, f'Sorry, only bot owner can run that command.') - except: + except Exception: await self.send_text(room, f'Module {command} experienced difficulty: {sys.exc_info()[0]} - see log for details') traceback.print_exc(file=sys.stderr) @@ -124,12 +130,13 @@ class Bot: result = await self.client.join(room.room_id) if type(result) == JoinError: print(f"Error joining room {room.room_id} (attempt %d): %s", - attempt, result.message, - ) + attempt, result.message, + ) else: break else: - print(f'Received invite event, but not joining as sender is not owner or bot not configured to join on invite. {event}') + print( + f'Received invite event, but not joining as sender is not owner or bot not configured to join on invite. {event}') def load_module(self, modulename): try: @@ -157,7 +164,7 @@ class Bot: if "matrix_poll" in dir(moduleobject): try: await moduleobject.matrix_poll(bot, self.pollcount) - except: + except Exception: traceback.print_exc(file=sys.stderr) await asyncio.sleep(10) @@ -178,16 +185,18 @@ class Bot: response = requests.get(ad_url) if response.status_code == 200: return response.json() - print(f'Getting account data failed: {response} {response.json()} - this is normal if you have not saved any settings yet.') + print( + f'Getting account data failed: {response} {response.json()} - this is normal if you have not saved any settings yet.') return None 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.join_on_invite = os.getenv("JOIN_ON_INVITE") is not None self.owners = os.environ['BOT_OWNERS'].split(',') self.get_modules() - + def stop(self): print(f'Stopping {len(self.modules)} modules..') for modulename, moduleobject in self.modules.items(): @@ -195,13 +204,14 @@ class Bot: if "matrix_stop" in dir(moduleobject): try: moduleobject.matrix_stop(bot) - except: + except Exception: traceback.print_exc(file=sys.stderr) async def run(self): if not self.client.access_token: await self.client.login(os.environ['MATRIX_PASSWORD']) - print("Logged in with password, access token:", self.client.access_token) + print("Logged in with password, access token:", + self.client.access_token) await self.client.sync() @@ -211,7 +221,7 @@ class Bot: if "matrix_start" in dir(moduleobject): try: moduleobject.matrix_start(bot) - except: + except Exception: traceback.print_exc(file=sys.stderr) self.poll_task = asyncio.get_event_loop().create_task(self.poll_timer()) diff --git a/modules/cron.py b/modules/cron.py index 8f0566f..af7fd2b 100644 --- a/modules/cron.py +++ b/modules/cron.py @@ -1,13 +1,14 @@ import shlex from datetime import datetime + class MatrixModule: - daily_commands = dict() # room_id -> command json + daily_commands = dict() # room_id -> command json last_hour = datetime.now().hour async def matrix_message(self, bot, room, event): bot.must_be_admin(room, event) - + args = shlex.split(event.body) args.pop(0) if len(args) == 3: @@ -16,7 +17,8 @@ class MatrixModule: dailycmd = args[2] if not self.daily_commands.get(room.room_id): self.daily_commands[room.room_id] = [] - self.daily_commands[room.room_id].append({ 'time': dailytime, 'command': dailycmd }) + self.daily_commands[room.room_id].append( + {'time': dailytime, 'command': dailycmd}) bot.save_settings() await bot.send_text(room, 'Daily command added.') elif len(args) == 1: @@ -31,7 +33,7 @@ class MatrixModule: return('Runs scheduled commands') def get_settings(self): - return { 'daily_commands': self.daily_commands } + return {'daily_commands': self.daily_commands} def set_settings(self, data): if data.get('daily_commands'): diff --git a/modules/googlecal.py b/modules/googlecal.py index 6a37460..45790b5 100644 --- a/modules/googlecal.py +++ b/modules/googlecal.py @@ -1,16 +1,13 @@ from __future__ import print_function -from datetime import datetime -import datetime -import pickle -import os.path -import time + import os +import os.path +import pickle +from datetime import datetime -from googleapiclient.discovery import build -from google_auth_oauthlib.flow import InstalledAppFlow from google.auth.transport.requests import Request - - +from google_auth_oauthlib.flow import InstalledAppFlow +from googleapiclient.discovery import build # # Google calendar notifications @@ -20,18 +17,19 @@ from google.auth.transport.requests import Request # can be copied to another computer. # + class MatrixModule: def matrix_start(self, 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] .. + 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 + return # No-op if not set up if os.path.exists('token.pickle'): with open('token.pickle', 'rb') as token: @@ -45,7 +43,8 @@ class MatrixModule: else: flow = InstalledAppFlow.from_client_secrets_file( self.credentials_file, self.SCOPES) - creds = flow.run_local_server(port=0) # urn:ietf:wg:oauth:2.0:oob + # 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) @@ -54,14 +53,15 @@ class MatrixModule: 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') + 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: + 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.') @@ -95,7 +95,8 @@ class MatrixModule: else: self.calendar_rooms[room.room_id] = [calid] - print(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}') + print( + f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}') bot.save_settings() @@ -111,7 +112,8 @@ class MatrixModule: 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)}') + print( + f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}') bot.save_settings() @@ -141,28 +143,29 @@ class MatrixModule: startTime = datetime.datetime.utcnow() now = startTime.isoformat() + 'Z' events_result = self.service.events().list(calendarId=calid, timeMin=now, - maxResults=10, singleEvents=True, - orderBy='startTime').execute() + maxResults=10, singleEvents=True, + orderBy='startTime').execute() events = events_result.get('items', []) return events def list_today(self, calid): startTime = datetime.datetime.utcnow() - startTime = startTime.replace(hour = 0, minute = 0, second = 0, microsecond=0) + startTime = startTime.replace( + hour=0, minute=0, second=0, microsecond=0) endTime = startTime + datetime.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() + 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): - return { 'calendar_rooms': self.calendar_rooms } + return {'calendar_rooms': self.calendar_rooms} def set_settings(self, data): if data.get('calendar_rooms'): diff --git a/modules/ig.py b/modules/ig.py index 347664a..7ee1cec 100644 --- a/modules/ig.py +++ b/modules/ig.py @@ -1,14 +1,17 @@ -from igramscraper.instagram import Instagram -from igramscraper.exception.instagram_not_found_exception import InstagramNotFoundException from datetime import datetime, timedelta from random import randrange +from igramscraper.exception.instagram_not_found_exception import \ + InstagramNotFoundException +from igramscraper.instagram import Instagram + + class MatrixModule: instagram = Instagram() known_ids = set() - account_rooms = dict() # Roomid -> [account, account..] - next_poll_time = dict() # Roomid -> datetime, None = not polled yet + account_rooms = dict() # Roomid -> [account, account..] + next_poll_time = dict() # Roomid -> datetime, None = not polled yet async def matrix_poll(self, bot, pollcount): if len(self.account_rooms): @@ -27,7 +30,8 @@ class MatrixModule: try: await self.poll_account(bot, account, roomid, send_messages) except InstagramNotFoundException: - print('ig error: there is ', account, ' account that does not exist - deleting from room') + print('ig error: there is ', account, + ' account that does not exist - deleting from room') self.account_rooms[roomid].remove(account) bot.save_settings() @@ -77,7 +81,8 @@ class MatrixModule: else: self.account_rooms[room.room_id] = [account] - print(f'Accounts now for this room {self.account_rooms.get(room.room_id)}') + print( + f'Accounts now for this room {self.account_rooms.get(room.room_id)}') try: await self.poll_account(bot, account, room.room_id, False) @@ -91,18 +96,20 @@ class MatrixModule: bot.must_be_admin(room, event) account = args[2] - print(f'Removing account {account} from room id {room.room_id}') + print( + f'Removing account {account} from room id {room.room_id}') if self.account_rooms.get(room.room_id): self.account_rooms[room.room_id].remove(account) - print(f'Accounts now for this room {self.account_rooms.get(room.room_id)}') + print( + f'Accounts now for this room {self.account_rooms.get(room.room_id)}') bot.save_settings() await bot.send_text(room, 'Removed instagram account from this room') def get_settings(self): - return { 'account_rooms': self.account_rooms } + return {'account_rooms': self.account_rooms} def set_settings(self, data): if data.get('account_rooms'): diff --git a/modules/loc.py b/modules/loc.py index a7cc85c..cced6af 100644 --- a/modules/loc.py +++ b/modules/loc.py @@ -1,8 +1,10 @@ from geopy.geocoders import Nominatim -from nio import (RoomMessageUnknown) +from nio import RoomMessageUnknown + class MatrixModule: bot = None + def matrix_start(self, bot): self.bot = bot bot.client.add_event_callback(self.unknown_cb, RoomMessageUnknown) @@ -26,8 +28,9 @@ class MatrixModule: float(latlon[0]) float(latlon[1]) - osm_link = 'https://www.openstreetmap.org/?mlat=' + latlon[0] + "&mlon=" + latlon[1] - + osm_link = 'https://www.openstreetmap.org/?mlat=' + \ + latlon[0] + "&mlon=" + latlon[1] + plain = sender + ' 🚩 ' + osm_link html = f'{sender} 🚩 {location_text}' diff --git a/modules/metar.py b/modules/metar.py index 11f5db9..ad94d15 100644 --- a/modules/metar.py +++ b/modules/metar.py @@ -1,13 +1,13 @@ -import subprocess -import os import urllib.request + class MatrixModule: async def matrix_message(self, bot, room, event): args = event.body.split() if len(args) == 2: icao = args[1] - metar_url = "https://tgftp.nws.noaa.gov/data/observations/metar/stations/" + icao.upper() + ".TXT" + metar_url = "https://tgftp.nws.noaa.gov/data/observations/metar/stations/" + \ + icao.upper() + ".TXT" response = urllib.request.urlopen(metar_url) lines = response.readlines() await bot.send_text(room, lines[1].decode("utf-8").strip()) diff --git a/modules/taf.py b/modules/taf.py index 35d4fe2..5704f77 100644 --- a/modules/taf.py +++ b/modules/taf.py @@ -1,6 +1,6 @@ -import os import urllib.request + class MatrixModule: async def matrix_message(self, bot, room, event): args = event.body.split() @@ -16,6 +16,6 @@ class MatrixModule: await bot.send_text(room, 'Cannot find taf for ' + icao) else: await bot.send_text(room, 'Usage: !taf ') - + def help(self): return('Taf data access (usage: !taf )') diff --git a/modules/teamup.py b/modules/teamup.py index 1a68a25..eddfbde 100644 --- a/modules/teamup.py +++ b/modules/teamup.py @@ -1,20 +1,22 @@ -from pyteamup import Calendar, Event -from datetime import datetime import time -import os +from datetime import datetime + +from pyteamup import Calendar # # TeamUp calendar notifications # + + class MatrixModule: api_key = None - calendar_rooms = dict() # Roomid -> [calid, calid..] - calendars = dict() # calid -> Calendar + calendar_rooms = dict() # Roomid -> [calid, calid..] + calendars = dict() # calid -> Calendar async def matrix_poll(self, bot, pollcount): if self.api_key: - if pollcount % (6 * 5) == 0: # Poll every 5 min - await self.poll_all_calendars(bot) + if pollcount % (6 * 5) == 0: # Poll every 5 min + await self.poll_all_calendars(bot) async def matrix_message(self, bot, room, event): args = event.body.split() @@ -24,10 +26,14 @@ class MatrixModule: calendar = self.calendars[calendarid] events = calendar.get_event_collection() for event in events: - s = '' + str(event.start_dt.day) + '.' + str(event.start_dt.month) + s = '' + str(event.start_dt.day) + \ + '.' + str(event.start_dt.month) if not event.all_day: - s = s + ' ' + event.start_dt.strftime("%H:%M") + ' (' + str(event.duration) + ' min)' - s = s + ' ' + event.title + " " + (event.notes or '') + s = s + ' ' + \ + event.start_dt.strftime( + "%H:%M") + ' (' + str(event.duration) + ' min)' + s = s + ' ' + event.title + \ + " " + (event.notes or '') await bot.send_html(room, s, s) elif len(args) == 2: if args[1] == 'list': @@ -51,7 +57,8 @@ class MatrixModule: else: self.calendar_rooms[room.room_id] = [calid] - print(f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}') + print( + f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}') bot.save_settings() self.setup_calendars() @@ -65,7 +72,8 @@ class MatrixModule: 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)}') + print( + f'Calendars now for this room {self.calendar_rooms.get(room.room_id)}') bot.save_settings() self.setup_calendars() @@ -85,12 +93,12 @@ class MatrixModule: for roomid in self.calendar_rooms: calendars = self.calendar_rooms[roomid] for calendarid in calendars: - events, timestamp = self.poll_server(self.calendars[calendarid]) + events, timestamp = self.poll_server( + self.calendars[calendarid]) self.calendars[calendarid].timestamp = timestamp for event in events: await bot.send_text(bot.get_room_by_id(roomid), 'Calendar: ' + self.eventToString(event)) - def poll_server(self, calendar): events, timestamp = calendar.get_changed_events(calendar.timestamp) return events, timestamp @@ -111,9 +119,12 @@ class MatrixModule: if(event['delete_dt']): s = event['title'] + ' deleted.' else: - s = event['title'] + " " + (event['notes'] or '') + ' ' + str(startdt.day) + '.' + str(startdt.month) + s = event['title'] + " " + (event['notes'] or '') + \ + ' ' + str(startdt.day) + '.' + str(startdt.month) if not event['all_day']: - s = s + ' ' + startdt.strftime("%H:%M") + ' (' + str(event['duration']) + ' min)' + s = s + ' ' + \ + startdt.strftime("%H:%M") + \ + ' (' + str(event['duration']) + ' min)' return s def setup_calendars(self): @@ -126,13 +137,13 @@ class MatrixModule: self.calendars[calid].timestamp = int(time.time()) def get_settings(self): - return { 'apikey': self.api_key or '', 'calendar_rooms': self.calendar_rooms } + return {'apikey': self.api_key or '', 'calendar_rooms': self.calendar_rooms} def set_settings(self, data): if data.get('calendar_rooms'): self.calendar_rooms = data['calendar_rooms'] if data.get('apikey'): self.api_key = data['apikey'] - if self.api_key and len(self.api_key)==0: + if self.api_key and len(self.api_key) == 0: self.api_key = None self.setup_calendars() diff --git a/modules/uptime.py b/modules/uptime.py index 4317c82..04495c5 100644 --- a/modules/uptime.py +++ b/modules/uptime.py @@ -1,5 +1,6 @@ import time + class MatrixModule: def matrix_start(self, bot): self.starttime = time.time() From 6b8e6bfbcdd48b5d4d74eed71ab77bec0bf17507 Mon Sep 17 00:00:00 2001 From: Tatu Wikman Date: Thu, 2 Jan 2020 14:27:48 +0200 Subject: [PATCH 5/5] and flake8 conf (mainly long lines) --- setup.cfg | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 setup.cfg diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..891a14d --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[flake8] +max-line-length = 260 + +[pycodestyle] +max-line-length = 260 \ No newline at end of file