linting with flake8 and pylint, code cleanup (#39)

* linting with flake8 and pylint

* add type hinting

* more linting

* and i oop

* revert caps

* update info

* remove unnecessary docstrings

* Update info.py
This commit is contained in:
Abigail Gold 2019-10-18 08:27:05 -04:00 committed by GitHub
parent e0b1e673dc
commit 769c93050b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 157 additions and 148 deletions

View File

@ -13,28 +13,29 @@ KE8FGB: assigned once, no restrictions
NA2AAA: unassigned, no records NA2AAA: unassigned, no records
""" """
from datetime import datetime
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
from datetime import datetime
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import aiohttp import aiohttp
class AE7QCog(commands.Cog): class AE7QCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
@commands.group(name="ae7q", aliases=["ae"]) @commands.group(name="ae7q", aliases=["ae"])
async def _ae7q_lookup(self, ctx): async def _ae7q_lookup(self, ctx: commands.Context):
'''Look up a callsign, FRN, or Licensee ID on ae7q.com''' '''Look up a callsign, FRN, or Licensee ID on ae7q.com'''
if ctx.invoked_subcommand is None: if ctx.invoked_subcommand is None:
await ctx.send('Invalid ae7q command passed\nPossible commands:' + await ctx.send('Invalid ae7q command passed\nPossible commands:' +
'`call`, `frn`, `lic` or `licensee`.') '`call`, `frn`, `lic` or `licensee`.')
@_ae7q_lookup.command(name="call") @_ae7q_lookup.command(name="call")
async def _ae7q_call(self, ctx, callsign: str): async def _ae7q_call(self, ctx: commands.Context, callsign: str):
callsign = callsign.upper() callsign = callsign.upper()
desc = '' desc = ''
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL=" base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
@ -52,7 +53,7 @@ class AE7QCog(commands.Cog):
rows = table.find_all("tr") rows = table.find_all("tr")
if len(rows) > 1 and len(rows[0]) > 1: if len(rows) > 1 and len(rows[0]) > 1:
break break
elif desc == '': if desc == '':
for row in rows: for row in rows:
desc += " ".join(row.getText().split()) desc += " ".join(row.getText().split())
desc += '\n' desc += '\n'
@ -75,19 +76,18 @@ class AE7QCog(commands.Cog):
for tr in rows: for tr in rows:
if rows.index(tr) == 0: if rows.index(tr) == 0:
continue continue
else: row_cells = []
row_cells = [] for td in tr.find_all('td'):
for td in tr.find_all('td'): if td.getText().strip() != '':
if td.getText().strip() != '': row_cells.append(td.getText().strip())
row_cells.append(td.getText().strip()) else:
else: row_cells.append('-')
row_cells.append('-') if 'colspan' in td.attrs and int(td.attrs['colspan']) > 1:
if 'colspan' in td.attrs and int(td.attrs['colspan']) > 1: for i in range(int(td.attrs['colspan']) - 1):
for i in range(int(td.attrs['colspan']) - 1): row_cells.append(row_cells[-1])
row_cells.append(row_cells[-1]) for i, cell in enumerate(row_cells):
for i in range(len(row_cells)): if cell == '"':
if row_cells[i] == '"': cell = table_contents[-1][i]
row_cells[i] = table_contents[-1][i]
if len(row_cells) > 1: if len(row_cells) > 1:
table_contents += [row_cells] table_contents += [row_cells]
@ -137,5 +137,5 @@ class AE7QCog(commands.Cog):
# pass # pass
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(AE7QCog(bot)) bot.add_cog(AE7QCog(bot))

View File

@ -7,14 +7,14 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
from datetime import datetime
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
from datetime import datetime
class BaseCog(commands.Cog): class BaseCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
@ -33,7 +33,8 @@ class BaseCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="ping") @commands.command(name="ping")
async def _ping(self, ctx): async def _ping(self, ctx: commands.Context):
"""Show the current latency to the discord endpoint."""
embed = discord.Embed(title="**Pong!**", embed = discord.Embed(title="**Pong!**",
description=f'Current ping is {self.bot.latency*1000:.1f} ms', description=f'Current ping is {self.bot.latency*1000:.1f} ms',
colour=self.gs.colours.neutral, colour=self.gs.colours.neutral,
@ -43,5 +44,5 @@ class BaseCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(BaseCog(bot)) bot.add_cog(BaseCog(bot))

View File

@ -7,30 +7,29 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import discord
import discord.ext.commands as commands import discord.ext.commands as commands
class FunCog(commands.Cog): class FunCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
@commands.command(name="xkcd", aliases=['x']) @commands.command(name="xkcd", aliases=['x'])
async def _xkcd(self, ctx, num : str): async def _xkcd(self, ctx: commands.Context, num: str):
'''Look up an xkcd by number.''' '''Look up an xkcd by number.'''
await ctx.send('http://xkcd.com/' + num) await ctx.send('http://xkcd.com/' + num)
@commands.command(name="tar") @commands.command(name="tar")
async def _tar(self, ctx): async def _tar(self, ctx: commands.Context):
'''Returns an xkcd about tar.''' '''Returns an xkcd about tar.'''
await ctx.send('http://xkcd.com/1168') await ctx.send('http://xkcd.com/1168')
@commands.command(name="xd") @commands.command(name="xd")
async def _xd(self, ctx): async def _xd(self, ctx: commands.Context):
'''ecks dee''' '''ecks dee'''
await ctx.send('ECKS DEE :smirk:') await ctx.send('ECKS DEE :smirk:')
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(FunCog(bot)) bot.add_cog(FunCog(bot))

View File

@ -7,20 +7,20 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import discord
import discord.ext.commands as commands
import math import math
from datetime import datetime from datetime import datetime
import discord
import discord.ext.commands as commands
class GridCog(commands.Cog): class GridCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
@commands.command(name="grid") @commands.command(name="grid")
async def _grid_sq_lookup(self, ctx, lat : str, lon : str): async def _grid_sq_lookup(self, ctx: commands.Context, lat: str, lon: str):
'''Calculates the grid square for latitude and longitude coordinates. '''Calculates the grid square for latitude and longitude coordinates.
Usage: `?grid <lat> <lon>` Usage: `?grid <lat> <lon>`
`lat` and `lon` are decimal coordinates, with negative being latitude South and longitude West.''' `lat` and `lon` are decimal coordinates, with negative being latitude South and longitude West.'''
@ -45,9 +45,9 @@ class GridCog(commands.Cog):
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
else: else:
raise ValueError('Out of range.') raise ValueError('Out of range.')
except Exception as e: except ValueError as err:
msg = f'Error generating grid square for {lat}, {lon}.' msg = f'Error generating grid square for {lat}, {lon}.'
embed = discord.Embed(title=msg, description=str(e), embed = discord.Embed(title=msg, description=str(err),
colour=self.gs.colours.bad, colour=self.gs.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -55,24 +55,26 @@ class GridCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="ungrid", aliases=['loc']) @commands.command(name="ungrid", aliases=['loc'])
async def _location_lookup(self, ctx, grid : str, grid2 : str = None): async def _location_lookup(self, ctx: commands.Context, grid: str, grid2: str = None):
'''Calculates the latitude and longitude for the center of a grid square. '''Calculates the latitude and longitude for the center of a grid square.
If two grid squares are given, the distance and azimuth between them is calculated.''' If two grid squares are given, the distance and azimuth between them is calculated.'''
with ctx.typing(): with ctx.typing():
if grid2 is None or grid2 == '': if grid2 is None or grid2 == '':
try: try:
grid = grid.upper() grid = grid.upper()
loc = self.getCoords(grid) loc = get_coords(grid)
if len(grid) >= 6: if len(grid) >= 6:
embed = discord.Embed(title=f'Latitude and Longitude for {grid}', embed = discord.Embed(title=f'Latitude and Longitude for {grid}',
description=f'**{loc[0]:.5f}, {loc[1]:.5f}**', colour=self.gs.colours.good, description=f'**{loc[0]:.5f}, {loc[1]:.5f}**',
colour=self.gs.colours.good,
url=f'https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}', url=f'https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}',
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
else: else:
embed = discord.Embed(title=f'Latitude and Longitude for {grid}', embed = discord.Embed(title=f'Latitude and Longitude for {grid}',
description=f'**{loc[0]:.1f}, {loc[1]:.1f}**', colour=self.gs.colours.good, description=f'**{loc[0]:.1f}, {loc[1]:.1f}**',
colour=self.gs.colours.good,
url=f'https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}', url=f'https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}',
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -85,28 +87,28 @@ class GridCog(commands.Cog):
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
else: else:
R = 6371 radius = 6371
try: try:
grid = grid.upper() grid = grid.upper()
grid2 = grid2.upper() grid2 = grid2.upper()
loc = self.getCoords(grid) loc = get_coords(grid)
loc2 = self.getCoords(grid2) loc2 = get_coords(grid2)
# Haversine formula # Haversine formula
dLat = math.radians(loc2[0] - loc[0]) d_lat = math.radians(loc2[0] - loc[0])
dLon = math.radians(loc2[1] - loc[1]) d_lon = math.radians(loc2[1] - loc[1])
a = math.sin(dLat/2) ** 2 +\ a = math.sin(d_lat/2) ** 2 +\
math.cos(math.radians(loc[0])) * math.cos(math.radians(loc2[0])) *\ math.cos(math.radians(loc[0])) * math.cos(math.radians(loc2[0])) *\
math.sin(dLon/2) ** 2 math.sin(d_lon/2) ** 2
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a)) c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
d = R * c d = radius * c
d_mi = 0.6213712 * d d_mi = 0.6213712 * d
# Bearing # Bearing
y = math.sin(math.radians(loc2[1]-loc[1])) * math.cos(math.radians(loc2[0])) y_dist = math.sin(math.radians(loc2[1]-loc[1])) * math.cos(math.radians(loc2[0]))
x = math.cos(math.radians(loc[0])) * math.sin(math.radians(loc2[0])) -\ x_dist = math.cos(math.radians(loc[0])) * math.sin(math.radians(loc2[0])) -\
math.sin(math.radians(loc[0])) * math.cos(math.radians(loc2[0])) *\ math.sin(math.radians(loc[0])) * math.cos(math.radians(loc2[0])) *\
math.cos(math.radians(loc2[1] - loc[1])) math.cos(math.radians(loc2[1] - loc[1]))
bearing = ( math.degrees(math.atan2(y, x)) + 360 ) % 360 bearing = (math.degrees(math.atan2(y_dist, x_dist)) + 360) % 360
des = f'**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°' des = f'**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°'
embed = discord.Embed(title=f'Great Circle Distance and Bearing from {grid} to {grid2}', embed = discord.Embed(title=f'Great Circle Distance and Bearing from {grid} to {grid2}',
@ -124,35 +126,35 @@ class GridCog(commands.Cog):
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
def getCoords(self, grid: str):
if len(grid) < 3:
raise ValueError('The grid locator must be at least 4 characters long.')
if not grid[0:2].isalpha() or not grid[2:4].isdigit(): def get_coords(grid: str):
if len(grid) <= 4: if len(grid) < 3:
raise ValueError('The grid locator must be of the form AA##.') raise ValueError('The grid locator must be at least 4 characters long.')
elif len(grid) >= 6 and not grid[5:7].isalpha():
raise ValueError('The grid locator must be of the form AA##AA.')
lon = ((ord(grid[0]) - ord('A')) * 20) - 180; if not grid[0:2].isalpha() or not grid[2:4].isdigit():
lat = ((ord(grid[1]) - ord('A')) * 10) - 90; if len(grid) <= 4:
lon += ((ord(grid[2]) - ord('0')) * 2); raise ValueError('The grid locator must be of the form AA##.')
lat += ((ord(grid[3]) - ord('0')) * 1); if len(grid) >= 6 and not grid[5:7].isalpha():
raise ValueError('The grid locator must be of the form AA##AA.')
if len(grid) >= 6: lon = ((ord(grid[0]) - ord('A')) * 20) - 180
# have subsquares lat = ((ord(grid[1]) - ord('A')) * 10) - 90
lon += ((ord(grid[4])) - ord('A')) * (5/60); lon += ((ord(grid[2]) - ord('0')) * 2)
lat += ((ord(grid[5])) - ord('A')) * (2.5/60); lat += ((ord(grid[3]) - ord('0')) * 1)
# move to center of subsquare
lon += (2.5/60); if len(grid) >= 6:
lat += (1.25/60); # have subsquares
return (lat, lon) lon += ((ord(grid[4])) - ord('A')) * (5/60)
else: lat += ((ord(grid[5])) - ord('A')) * (2.5/60)
# move to center of square # move to center of subsquare
lon += 1; lon += (2.5/60)
lat += 0.5; lat += (1.25/60)
return (lat, lon) return (lat, lon)
# move to center of square
lon += 1
lat += 0.5
return (lat, lon)
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(GridCog(bot)) bot.add_cog(GridCog(bot))

View File

@ -6,34 +6,34 @@ Copyright (C) 2019 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import discord
import discord.ext.commands as commands
import json import json
import random import random
from datetime import datetime from datetime import datetime
import discord
import discord.ext.commands as commands
class HamCog(commands.Cog): class HamCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
with open('resources/qcodes.json') as qcode_file: with open('resources/qcodes.json') as qcode_file:
self.qcodes = json.load(qcode_file) self.qcodes = json.load(qcode_file)
with open('resources/words') as words_file: with open('resources/words') as words_file:
self.WORDS = words_file.read().lower().splitlines() self.words = words_file.read().lower().splitlines()
@commands.command(name="qcode", aliases=['q']) @commands.command(name="qcode", aliases=['q'])
async def _qcode_lookup(self, ctx, q : str): async def _qcode_lookup(self, ctx: commands.Context, qcode: str):
'''Look up a Q Code.''' '''Look up a Q Code.'''
with ctx.typing(): with ctx.typing():
q = q.upper() qcode = qcode.upper()
if q in self.qcodes: if qcode in self.qcodes:
embed = discord.Embed(title=q, description=self.qcodes[q], embed = discord.Embed(title=qcode, description=self.qcodes[qcode],
colour=self.gs.colours.good, colour=self.gs.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
else: else:
embed = discord.Embed(title=f'Q Code {q} not found', embed = discord.Embed(title=f'Q Code {qcode} not found',
colour=self.gs.colours.bad, colour=self.gs.colours.bad,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
@ -41,14 +41,13 @@ class HamCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="phonetics", aliases=['ph', 'phoneticize', 'phoneticise', 'phone']) @commands.command(name="phonetics", aliases=['ph', 'phoneticize', 'phoneticise', 'phone'])
async def _phonetics_lookup(self, ctx, *, msg: str): async def _phonetics_lookup(self, ctx: commands.Context, *, msg: str):
'''Get phonetics for a word or phrase.''' '''Get phonetics for a word or phrase.'''
with ctx.typing(): with ctx.typing():
result = '' result = ''
for char in msg.lower(): for char in msg.lower():
if char.isalpha(): if char.isalpha():
w = [word for word in self.WORDS if (word[0] == char)] result += random.choice([word for word in self.words if word[0] == char])
result += random.choice(w)
else: else:
result += char result += char
result += ' ' result += ' '
@ -61,11 +60,11 @@ class HamCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="utc", aliases=['z']) @commands.command(name="utc", aliases=['z'])
async def _utc_lookup(self, ctx): async def _utc_lookup(self, ctx: commands.Context):
'''Gets the current time in UTC.''' '''Gets the current time in UTC.'''
with ctx.typing(): with ctx.typing():
d = datetime.utcnow() now = datetime.utcnow()
result = '**' + d.strftime('%Y-%m-%d %H:%M') + 'Z**' result = '**' + now.strftime('%Y-%m-%d %H:%M') + 'Z**'
embed = discord.Embed(title='The current time is:', embed = discord.Embed(title='The current time is:',
description=result, description=result,
colour=self.gs.colours.good, colour=self.gs.colours.good,
@ -75,5 +74,5 @@ class HamCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(HamCog(bot)) bot.add_cog(HamCog(bot))

View File

@ -7,21 +7,22 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import io
from datetime import datetime
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import aiohttp import aiohttp
import io
from datetime import datetime
class ImageCog(commands.Cog): class ImageCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
@commands.command(name="plan", aliases=['bands']) @commands.command(name="plan", aliases=['bands'])
async def _bandplan(self, ctx, msg: str = ''): async def _bandplan(self, ctx: commands.Context, msg: str = ''):
'''Posts an image of Frequency Allocations. '''Posts an image of Frequency Allocations.
Optional argument: `cn`, `ca`, `nl`, `us`, `mx`.''' Optional argument: `cn`, `ca`, `nl`, `us`, `mx`.'''
name = {'cn': 'Chinese', name = {'cn': 'Chinese',
@ -46,41 +47,47 @@ class ImageCog(commands.Cog):
await ctx.send(embed=embed, file=img) await ctx.send(embed=embed, file=img)
@commands.command(name="cond", aliases=['condx']) @commands.command(name="cond", aliases=['condx'])
async def _band_conditions(self, ctx, msg: str = ''): async def _band_conditions(self, ctx: commands.Context):
'''Posts an image of HF Band Conditions.''' '''Posts an image of HF Band Conditions.'''
with ctx.typing(): with ctx.typing():
async with aiohttp.ClientSession() as session:
async with session.get('http://www.hamqsl.com/solarsun.php') as resp:
if resp.status != 200:
return await ctx.send('Could not download file...')
data = io.BytesIO(await resp.read())
embed = discord.Embed(title='Current Solar Conditions', embed = discord.Embed(title='Current Solar Conditions',
colour=self.gs.colours.good, colour=self.gs.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_image(url=f'attachment://condx.png') async with aiohttp.ClientSession() as session:
async with session.get('http://www.hamqsl.com/solarsun.php') as resp:
if resp.status != 200:
embed.description = 'Could not download file...'
embed.colour = self.gs.colours.bad
else:
data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://condx.png')
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, 'condx.png')) await ctx.send(embed=embed, file=discord.File(data, 'condx.png'))
@commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl']) @commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl'])
async def _grayline(self, ctx, msg: str = ''): async def _grayline(self, ctx: commands.Context):
'''Posts a map of the current greyline, where HF propagation is the best.''' '''Posts a map of the current greyline, where HF propagation is the best.'''
gl_url = ('http://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=NOAAtopo.evif'
'&imgsize=320&dynimg=y&opt=-p&lat=&lon=&alt=&tle=&date=0&utc=&jd=')
with ctx.typing(): with ctx.typing():
async with aiohttp.ClientSession() as session:
async with session.get('http://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=NOAAtopo.evif&imgsize=320&dynimg=y&opt=-p&lat=&lon=&alt=&tle=&date=0&utc=&jd=') as resp:
if resp.status != 200:
return await ctx.send('Could not download file...')
data = io.BytesIO(await resp.read())
embed = discord.Embed(title='Current Greyline Conditions', embed = discord.Embed(title='Current Greyline Conditions',
colour=self.gs.colours.good, colour=self.gs.colours.good,
timestamp=datetime.utcnow()) timestamp=datetime.utcnow())
embed.set_image(url=f'attachment://greyline.jpg') async with aiohttp.ClientSession() as session:
async with session.get(gl_url) as resp:
if resp.status != 200:
embed.description = 'Could not download file...'
embed.colour = self.gs.colours.bad
else:
data = io.BytesIO(await resp.read())
embed.set_image(url=f'attachment://greyline.jpg')
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, 'greyline.jpg')) await ctx.send(embed=embed, file=discord.File(data, 'greyline.jpg'))
@commands.command(name="map") @commands.command(name="map")
async def _map(self, ctx, msg: str = ''): async def _map(self, ctx: commands.Context, msg: str = ''):
'''Posts an image of Frequency Allocations. '''Posts an image of Frequency Allocations.
Optional argument:`cq` = CQ Zones, `itu` = ITU Zones, `arrl` or `rac` = Optional argument:`cq` = CQ Zones, `itu` = ITU Zones, `arrl` or `rac` =
ARRL/RAC sections, `cn` = Chinese Callsign Areas, `us` = US Callsign Areas.''' ARRL/RAC sections, `cn` = Chinese Callsign Areas, `us` = US Callsign Areas.'''
@ -107,5 +114,5 @@ class ImageCog(commands.Cog):
await ctx.send(embed=embed, file=img) await ctx.send(embed=embed, file=img)
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(ImageCog(bot)) bot.add_cog(ImageCog(bot))

View File

@ -7,15 +7,15 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import discord
import discord.ext.commands as commands
import json import json
from datetime import datetime from datetime import datetime
import discord
import discord.ext.commands as commands
class MorseCog(commands.Cog): class MorseCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
with open('resources/morse.json') as morse_file: with open('resources/morse.json') as morse_file:
@ -23,7 +23,7 @@ class MorseCog(commands.Cog):
self.morse2ascii = {v: k for k, v in self.ascii2morse.items()} self.morse2ascii = {v: k for k, v in self.ascii2morse.items()}
@commands.command(name="morse", aliases=['cw']) @commands.command(name="morse", aliases=['cw'])
async def _morse(self, ctx, *, msg: str): async def _morse(self, ctx: commands.Context, *, msg: str):
"""Converts ASCII to international morse code.""" """Converts ASCII to international morse code."""
with ctx.typing(): with ctx.typing():
result = '' result = ''
@ -42,7 +42,7 @@ class MorseCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="unmorse", aliases=['demorse', 'uncw', 'decw']) @commands.command(name="unmorse", aliases=['demorse', 'uncw', 'decw'])
async def _unmorse(self, ctx, *, msg: str): async def _unmorse(self, ctx: commands.Context, *, msg: str):
'''Converts international morse code to ASCII.''' '''Converts international morse code to ASCII.'''
with ctx.typing(): with ctx.typing():
result = '' result = ''
@ -65,15 +65,15 @@ class MorseCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="weight", aliases=["cwweight", 'cww']) @commands.command(name="weight", aliases=["cwweight", 'cww'])
async def _weight(self, ctx, msg: str): async def _weight(self, ctx: commands.Context, msg: str):
'''Calculates the CW Weight of a callsign.''' '''Calculates the CW Weight of a callsign.'''
with ctx.typing(): with ctx.typing():
msg = msg.upper() msg = msg.upper()
weight = 0 weight = 0
for char in msg: for char in msg:
try: try:
cwChar = self.ascii2morse[char].replace('-', '==') cw_char = self.ascii2morse[char].replace('-', '==')
weight += len(cwChar) * 2 + 2 weight += len(cw_char) * 2 + 2
except KeyError: except KeyError:
res = f'Unknown character {char} in callsign' res = f'Unknown character {char} in callsign'
await ctx.send(res) await ctx.send(res)
@ -88,5 +88,5 @@ class MorseCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(MorseCog(bot)) bot.add_cog(MorseCog(bot))

View File

@ -7,23 +7,24 @@ This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import random
import json
from datetime import datetime
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import random
import json
import aiohttp import aiohttp
from datetime import datetime
class StudyCog(commands.Cog): class StudyCog(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.gs = bot.get_cog("GlobalSettings") self.gs = bot.get_cog("GlobalSettings")
self.lastq = dict() self.lastq = dict()
@commands.command(name="rq", aliases=['randomq']) @commands.command(name="rq", aliases=['randomq'])
async def _random_question(self, ctx, level: str = None): async def _random_question(self, ctx: commands.Context, level: str = None):
'''Gets a random question from the Technician, General, and/or Extra question pools.''' '''Gets a random question from the Technician, General, and/or Extra question pools.'''
tech_pool = 'E2_2018' tech_pool = 'E2_2018'
gen_pool = 'E3_2019' gen_pool = 'E3_2019'
@ -45,12 +46,12 @@ class StudyCog(commands.Cog):
if level in ['e', 'ae', 'extra']: if level in ['e', 'ae', 'extra']:
selected_pool = extra_pool selected_pool = extra_pool
if (level is None) or (level == 'all'): # no pool given or user wants all, so pick a random pool and use that if (level is None) or (level == 'all'): # no pool given or user wants all, so pick a random pool
selected_pool = random.choice([tech_pool, gen_pool, extra_pool]) selected_pool = random.choice([tech_pool, gen_pool, extra_pool])
if (level is not None) and (selected_pool is None): # unrecognized pool given by user if (level is not None) and (selected_pool is None): # unrecognized pool given by user
await ctx.send('The question pool you gave was unrecognized. ' + await ctx.send('The question pool you gave was unrecognized. ' +
'There are many ways to call up certain question pools - try ?rq t, g, or e. ' + 'There are many ways to call up certain question pools - try ?rq t, g, or e. ' +
'(Note that only the US question pools are available).') '(Note that only the US question pools are available).')
return return
async with aiohttp.ClientSession() as session: async with aiohttp.ClientSession() as session:
@ -70,11 +71,11 @@ class StudyCog(commands.Cog):
embed.set_footer(text=ctx.author.name, embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url)) icon_url=str(ctx.author.avatar_url))
embed = embed.add_field(name='Question:', value=question['text'], inline=False) embed = embed.add_field(name='Question:', value=question['text'], inline=False)
embed = embed.add_field(name='Answers:', value= embed = embed.add_field(name='Answers:', value='**A:** ' + question['answers']['A'] +
'**A:** ' + question['answers']['A'] +
'\n**B:** ' + question['answers']['B'] + '\n**B:** ' + question['answers']['B'] +
'\n**C:** ' + question['answers']['C'] + '\n**C:** ' + question['answers']['C'] +
'\n**D:** ' + question['answers']['D'], inline=False) '\n**D:** ' + question['answers']['D'],
inline=False)
embed = embed.add_field(name='Answer:', value='Type _?rqa_ for answer', inline=False) embed = embed.add_field(name='Answer:', value='Type _?rqa_ for answer', inline=False)
if 'image' in question: if 'image' in question:
image_url = f'https://hamstudy.org/_1330011/images/{selected_pool.split("_",1)[1]}/{question["image"]}' image_url = f'https://hamstudy.org/_1330011/images/{selected_pool.split("_",1)[1]}/{question["image"]}'
@ -83,7 +84,7 @@ class StudyCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="rqa") @commands.command(name="rqa")
async def _q_answer(self, ctx, ans: str = None): async def _q_answer(self, ctx: commands.Context, ans: str = None):
'''Returns the answer to question last asked (Optional argument: your answer).''' '''Returns the answer to question last asked (Optional argument: your answer).'''
with ctx.typing(): with ctx.typing():
correct_ans = self.lastq[ctx.message.channel.id][1] correct_ans = self.lastq[ctx.message.channel.id][1]
@ -114,5 +115,5 @@ class StudyCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
def setup(bot): def setup(bot: commands.Bot):
bot.add_cog(StudyCog(bot)) bot.add_cog(StudyCog(bot))

View File

@ -19,7 +19,7 @@ General Public License, version 2.
""" """
authors = ("@ClassAbbyAmplifier#2229", "@0x5c") authors = ("@ClassAbbyAmplifier#2229", "@0x5c")
description = """A description goes here.""" description = """A bot with various useful ham radio-related functions, written in Python."""
license = "A license goes here." license = "Released under the GNU General Public License v2"
contributing = "Info on contributing goes here." contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm-bot"
release_timestamp = "not yet :P" release_timestamp = "not yet :P"

12
main.py
View File

@ -27,7 +27,7 @@ debug_mode = opt.debug # Separate assignement in-case we define an override (te
class GlobalSettings(commands.Cog): class GlobalSettings(commands.Cog):
def __init__(self, bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.opt = opt self.opt = opt
@ -46,6 +46,7 @@ bot = commands.Bot(command_prefix=opt.prefix,
description=info.description, description=info.description,
help_command=commands.MinimalHelpCommand()) help_command=commands.MinimalHelpCommand())
# --- Helper functions --- # --- Helper functions ---
async def add_react(msg: discord.Message, react: str): async def add_react(msg: discord.Message, react: str):
@ -60,16 +61,15 @@ async def add_react(msg: discord.Message, react: str):
async def check_if_owner(ctx: commands.Context): async def check_if_owner(ctx: commands.Context):
if ctx.author.id in opt.owners_uids: if ctx.author.id in opt.owners_uids:
return True return True
else: await add_react(ctx.message, "")
await add_react(ctx.message, "") return False
return False
# --- Commands --- # --- Commands ---
@bot.command(name="restart") @bot.command(name="restart")
@commands.check(check_if_owner) @commands.check(check_if_owner)
async def _restart_bot(ctx): async def _restart_bot(ctx: commands.Context):
"""Restarts the bot.""" """Restarts the bot."""
global exit_code global exit_code
await add_react(ctx.message, "") await add_react(ctx.message, "")
@ -79,7 +79,7 @@ async def _restart_bot(ctx):
@bot.command(name="shutdown") @bot.command(name="shutdown")
@commands.check(check_if_owner) @commands.check(check_if_owner)
async def _shutdown_bot(ctx): async def _shutdown_bot(ctx: commands.Context):
"""Shuts down the bot.""" """Shuts down the bot."""
global exit_code global exit_code
await add_react(ctx.message, "") await add_react(ctx.message, "")