59 Commits

Author SHA1 Message Date
Abigail Gold c7d0a85464 Merge pull request #149 from classabbyamp/changelogv2.1.0
v2.1.0 prep for release
2020-01-04 02:24:31 -05:00
Abigail Gold ad11af2865 bumped version to v2.1.0 2020-01-04 02:18:06 -05:00
0x5c 2f0084d450 Changelog for 2.1.0 2020-01-04 02:13:06 -05:00
0x5c e1d4b2eab5 Merge pull request #147 from classabbyamp/error-handling
Better command error handling
2020-01-04 01:55:42 -05:00
Abigail Gold 09323e54bc add command to link to the issue tracker 2020-01-04 01:49:37 -05:00
Abigail Gold f974bf1223 beep 2020-01-04 01:40:17 -05:00
0x5c 0615df0829 Better command error handling
- Added handling for all subclasses of CommandError
- Added more emojis
- Renamed some emojis
- debug_mode -> bot.qrm.debug_mode

Fixes #22

Co-authored-by: Abigail Gold <dev@kb6.ee>
2020-01-03 23:03:17 -05:00
0x5c 70505f10f6 Merge pull request #143 from classabbyamp/5c-fixclose
Fix unclosed files (BytesIO)
2019-12-31 03:34:36 -05:00
0x5c 807ef46379 Fix unclosed files (BytesIO)
Fixes #119
2019-12-31 03:18:30 -05:00
Abigail Gold f32aa240ce Merge pull request #138 from classabbyamp/continuations
correct some line continuations
2019-12-31 01:16:13 -05:00
Abigail 74558c7f27 fix regression in grid (continuations) 2019-12-31 01:13:45 -05:00
Abigail Gold ea8c3a9045 in dent we trust 2019-12-25 02:52:14 -05:00
Abigail b0072f779e addressing the issue for once 2019-12-25 02:48:59 -05:00
Abigail 69d36fea7f oop 2019-12-25 02:30:03 -05:00
Abigail Gold 5e250b5e3e Merge pull request #137 from classabbyamp/phonetics
Phonetics refacAtor
2019-12-25 02:15:04 -05:00
Abigail Gold 48e5040a87 Merge pull request #136 from classabbyamp/info-server
official server in info command
2019-12-25 02:00:32 -05:00
Abigail aa73f575b5 change readme invite 2019-12-25 01:55:25 -05:00
Abigail 4368e0b603 correct some line continuations
Fixes #43
2019-12-25 01:42:04 -05:00
Abigail Gold 2e985209a8 remove qcodes file from flake8 checking 2019-12-25 00:57:15 -05:00
Abigail 0b292eda10 make phonetics cmd into funetics, create phonetics cmd, convert morse
and qcode json to python dicts

Fixes #122
Fixes #123
2019-12-25 00:42:54 -05:00
Abigail 4bf398a0de update shield in README (change shield graphic and fixed invite link) 2019-12-24 21:12:59 -05:00
Abigail 3da67ad6a2 add official server to info command.
Fixes #134
2019-12-24 21:04:59 -05:00
Abigail Gold 02192a7ea9 Merge pull request #132 from classabbyamp/5c-metadata
Metadata update!
2019-12-24 10:32:32 -05:00
Abigail Gold f4806f8a9e Merge pull request #133 from classabbyamp/5c-microlint
Removed an unused f-string
2019-12-24 10:20:38 -05:00
0x5c 3196aa6e40 Removed an unused f-string 2019-12-23 21:29:50 -05:00
0x5c 309f9f7ecb F**ing delayed linter 2019-12-23 21:17:59 -05:00
0x5c 1b21583d41 Merge branch 'master' into 5c-metadata
Fix conflicts
2019-12-23 21:13:01 -05:00
0x5c 3ab8fae80a Removed extraneous blank lines 2019-12-23 21:02:55 -05:00
0x5c 96b2a8f259 Metadata update!
- Added flag emojis to commands with countries.
- Added image attribution and description to "image" commands.
- Added file metadata to image resources.
- Added new key in options.py: pika.
- Added classes to deal with file metadata.
- Added common paths to common.py.
- Changed directory/file names of resources.
- Documented the new metadata format.

Fixes #83 - Metadata storage
Fixes #86 - Country flags
2019-12-23 20:51:31 -05:00
Abigail Gold 7d1a2a561b Merge pull request #131 from classabbyamp/aiohttp-sessions
combine all aiohttp operations into a single session
2019-12-23 18:12:18 -05:00
Abigail f7aaa467b5 >_> pep8 2019-12-23 18:10:36 -05:00
Abigail f1993c85b2 combine all aiohttp operations into a single session
Fixes #49
2019-12-23 17:54:20 -05:00
Abigail Gold 0bc368877b Merge pull request #129 from classabbyamp/dynamic-status
modify playing status so it changes based on time of day
2019-12-23 16:01:09 -05:00
Abigail 28eb6d45c1 updates from review 2019-12-23 15:57:57 -05:00
Abigail Gold 951dc38992 Update main.py
Co-Authored-By: 0x5c <0x5c.dev@gmail.com>
2019-12-23 15:56:25 -05:00
Abigail Gold f0805d2095 Merge pull request #128 from classabbyamp/qrz-incomplete-fix
qrz: handle addresses that are only whitespace
2019-12-23 15:45:28 -05:00
Abigail 4d763c2fc1 sigh 2019-12-23 14:08:05 -05:00
Abigail b25b5a95ff refactor to allow for fixed, random, or by time statuses 2019-12-23 14:06:42 -05:00
Abigail 9e79eececc remove print statement 2019-12-23 13:34:56 -05:00
Abigail 2ccb225a5f hhhhhhhh whitespace 2019-12-23 13:33:19 -05:00
Abigail f649d15890 modify playing status so it changes based on time of day
Fixes #57
2019-12-23 13:29:04 -05:00
Abigail e9d2274e64 remove print statement 2019-12-23 11:49:55 -05:00
Abigail ae67498e75 qrz: handle addresses that are only whitespace
Fixes #124
2019-12-23 11:47:37 -05:00
Abigail Gold 781a3fa4dd Merge pull request #127 from classabbyamp/reactions
add reactions on keywords functionality
2019-12-23 11:36:14 -05:00
Abigail c48a4069cb add reactions on keywords functionality
Fixes #52
2019-12-23 11:32:02 -05:00
Abigail Gold e64c9ba2df Merge pull request #125 from classabbyamp/embed-add-syntax
change embed = embed.add_field() to embed.add_field()
2019-12-23 10:41:37 -05:00
Abigail Gold 5c95b9ff12 Merge pull request #126 from classabbyamp/all-embedded
fix commands that do not use embeds
2019-12-23 10:40:30 -05:00
Abigail ca224cc744 fix commands that do not use embeds
Fixes #50
2019-12-23 10:32:24 -05:00
Abigail a93f6d7a03 >:-( PEP8 2019-12-23 09:57:05 -05:00
Abigail d3fd36fb75 change embed = embed.add_field() to embed.add_field(). Fixes #106. 2019-12-23 09:53:49 -05:00
Abigail Gold c47fd0549d Merge pull request #121 from classabbyamp/server-shield
add server shield to readme
2019-12-18 11:54:26 -05:00
Abigail Gold d7f50bc083 add server shield to readme 2019-12-18 11:47:47 -05:00
Abigail Gold e84ba201bd Merge pull request #118 from classabbyamp/embed-factory
convert all embed creation to embed_factory
2019-12-18 10:35:15 -05:00
Abigail Gold 26765e86b6 Merge pull request #104 from classabbyamp/echo-cmd
add command to echo a message as the bot in a channel
2019-12-18 10:21:17 -05:00
Abigail 51cd182c57 PEP8, my nemesis, we meet again! (removed unused imports) 2019-12-16 03:59:14 -05:00
Abigail 7a9d64465e convert all embed creation to embed_factory 2019-12-16 03:49:34 -05:00
Abigail Gold 5beb68e1c5 Merge pull request #117 from bruceyang1998/patch-1
Update callsign_info.py
2019-12-16 01:31:31 -05:00
Bruce Yang 5da9cf0bf2 Update callsign_info.py
Fix Group B (was identical to Group A)
Add detail about some unavailable calls
2019-12-16 01:20:13 -05:00
Abigail ba1b7e947f add command to echo a message as the bot in a channel.
Command errors are not handled because that will be done in #22. Fixes #77
2019-12-10 00:47:40 -05:00
38 changed files with 948 additions and 765 deletions
+24 -1
View File
@@ -3,8 +3,28 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
## [v2.1.0] - 2020-01-04
### Added
- New NATO "phonetics" command.
- Flag emojis to commands with countries.
- Image attribution and description to "image" commands.
- Better user-facing command error handling.
- Reacts with emojis upon specific keywords.
- Official server to info command.
- Command linking to the issue tracker.
- New key in options.py: pika.
### Changed
- The "phonetics" command is not called "funetics".
- All commands now respond in embeds.
- Playing status can now change on a schedule or randomly from a list.
### Fixed
- Fixed incorrect information in the `prefixes` command.
## [v2.0.0] - 2019-12-16 ## [v2.0.0] - 2019-12-16
### Added ### Added
- Rich lookup for AE7Q.com (callsigns only, more to come) - Rich lookup for AE7Q.com (callsigns only, more to come)
@@ -31,7 +51,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Cleaned up code to comply with the PEP8 Standard - Cleaned up code to comply with the PEP8 Standard
- Issue in morse and unmorse commands where spaces were not interpreted correctly - Issue in morse and unmorse commands where spaces were not interpreted correctly
## v1.0.0 - 2019-07-31 [YANKED] ## v1.0.0 - 2019-07-31 [YANKED]
[Unreleased]: https://github.com/classabbyamp/discord-qrm2/compare/v2.0.0...HEAD
[Unreleased]: https://github.com/classabbyamp/discord-qrm2/compare/v2.1.0...HEAD
[v2.1.0]: https://github.com/classabbyamp/discord-qrm2/releases/tag/v2.1.0
[v2.0.0]: https://github.com/classabbyamp/discord-qrm2/releases/tag/v2.0.0 [v2.0.0]: https://github.com/classabbyamp/discord-qrm2/releases/tag/v2.0.0
+2
View File
@@ -1,5 +1,7 @@
# qrm, a Bot for Discord # qrm, a Bot for Discord
[![Discord](https://discordapp.com/api/guilds/656888365886734340/widget.png?style=shield)](https://discord.gg/SwyjdDN)
A discord bot with ham radio functionalities. A discord bot with ham radio functionalities.
An independent rewrite of qrmbot-discord. An independent rewrite of qrmbot-discord.
+65 -11
View File
@@ -13,18 +13,20 @@ General Public License, version 2.
""" """
import collections
import json
import traceback import traceback
from pathlib import Path
from datetime import datetime from datetime import datetime
from types import SimpleNamespace from types import SimpleNamespace
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import data.options as opt import data.options as opt
__all__ = ["colours", "cat", "emojis", "error_embed_factory", "add_react", "check_if_owner"] __all__ = ["colours", "cat", "emojis", "embed_factory", "error_embed_factory", "add_react", "check_if_owner"]
# --- Common values --- # --- Common values ---
@@ -40,24 +42,77 @@ cat = SimpleNamespace(lookup='Information Lookup',
study='Exam Study', study='Exam Study',
weather='Land and Space Weather') weather='Land and Space Weather')
emojis = SimpleNamespace(good='', emojis = SimpleNamespace(check_mark='',
bad='') x='',
warning='⚠️',
question='',
no_entry='',
bangbang='‼️')
paths = SimpleNamespace(data=Path("./data/"),
resources=Path("./resources/"),
bandcharts=Path("./resources/img/bandcharts/"),
maps=Path("./resources/img/maps/"))
# --- Classes ---
class ImageMetadata:
"""Represents the metadata of a single image."""
def __init__(self, metadata: list):
self.filename: str = metadata[0]
self.name: str = metadata[1]
self.long_name: str = metadata[2]
self.description: str = metadata[3]
self.source: str = metadata[4]
self.emoji: str = metadata[5]
class ImagesGroup(collections.abc.Mapping):
"""Represents a group of images, loaded from a meta.json file."""
def __init__(self, file_path):
self._images = {}
self.path = file_path
with open(file_path, "r") as file:
images: dict = json.load(file)
for key, imgdata in images.items():
self._images[key] = ImageMetadata(imgdata)
# Wrappers to implement dict-like functionality
def __len__(self):
return len(self._images)
def __getitem__(self, key: str):
return self._images[key]
def __iter__(self):
return iter(self._images)
# str(): Simply return what it would be for the underlaying dict
def __str__(self):
return str(self._images)
# --- Helper functions --- # --- Helper functions ---
def embed_factory(ctx: commands.Context) -> discord.Embed:
"""Creates an embed with neutral colour and standard footer."""
embed = discord.Embed(timestamp=datetime.utcnow(), colour=colours.neutral)
embed.set_footer(text=ctx.author, icon_url=str(ctx.author.avatar_url))
return embed
def error_embed_factory(ctx: commands.Context, exception: Exception, debug_mode: bool) -> discord.Embed: def error_embed_factory(ctx: commands.Context, exception: Exception, debug_mode: bool) -> discord.Embed:
"""Creates an Error embed.""" """Creates an Error embed."""
if debug_mode: if debug_mode:
fmtd_ex = traceback.format_exception(exception.__class__, exception, exception.__traceback__) fmtd_ex = traceback.format_exception(exception.__class__, exception, exception.__traceback__)
else: else:
fmtd_ex = traceback.format_exception_only(exception.__class__, exception) fmtd_ex = traceback.format_exception_only(exception.__class__, exception)
embed = discord.Embed(title="Error", embed = embed_factory(ctx)
timestamp=datetime.utcnow(), embed.title = "⚠️ Error"
colour=colours.bad)
embed.set_footer(text=ctx.author,
icon_url=str(ctx.author.avatar_url))
embed.description = "```\n" + '\n'.join(fmtd_ex) + "```" embed.description = "```\n" + '\n'.join(fmtd_ex) + "```"
embed.colour = colours.bad
return embed return embed
@@ -73,5 +128,4 @@ 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
await add_react(ctx.message, emojis.bad) raise commands.NotOwner
return False
+24
View File
@@ -0,0 +1,24 @@
# File metadata format
Used for grouping info such as name, description, source, and such.
## Format
*`id` is the dictionary key, and the list items are the attributes for each files.*
`meta.json`
```json
{
"id": ["filename", "name", "long_name", "description", "source", "emoji"]
}
```
| Attribute | Description | Examples |
| ------------- | ----------------------------------------------- | ------------------------------------------------ |
| `filename` | The file name of the file, without the path. | `ca.png`, `itu.png` |
| `name` | The name of the file. | `Canada`, `ITU Zones` |
| `long_name` | The long name (title) of the file. | `Worldwide map of ITU Zones` |
| `description` | The description accompanying the file. | `Full radio allocations chart for all services.` |
| `source` | The source of the file. | `Instituto Federal de Telecomunicaciones (IFT)` |
| `emoji` | A Unicode emoji associated with the file. | `📻`, `🇨🇦` |
+25 -31
View File
@@ -13,13 +13,9 @@ KE8FGB: assigned once, no restrictions
NA2AAA: unassigned, no records NA2AAA: unassigned, no records
""" """
from datetime import datetime
import discord
import discord.ext.commands as commands import discord.ext.commands as commands
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
import aiohttp
import common as cmn import common as cmn
@@ -27,6 +23,7 @@ import common as cmn
class AE7QCog(commands.Cog): class AE7QCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.session = bot.qrm.session
@commands.group(name="ae7q", aliases=["ae"], category=cmn.cat.lookup) @commands.group(name="ae7q", aliases=["ae"], category=cmn.cat.lookup)
async def _ae7q_lookup(self, ctx: commands.Context): async def _ae7q_lookup(self, ctx: commands.Context):
@@ -40,12 +37,16 @@ class AE7QCog(commands.Cog):
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="
embed = cmn.embed_factory(ctx)
async with aiohttp.ClientSession() as session: async with self.session.get(base_url + callsign) as resp:
async with session.get(base_url + callsign) as resp: if resp.status != 200:
if resp.status != 200: embed.title = "Error in AE7Q call command"
return await ctx.send('Could not load AE7Q') embed.description = 'Could not load AE7Q'
page = await resp.text() embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
return
page = await resp.text()
soup = BeautifulSoup(page, features="html.parser") soup = BeautifulSoup(page, features="html.parser")
tables = soup.select("table.Database") tables = soup.select("table.Database")
@@ -62,12 +63,9 @@ class AE7QCog(commands.Cog):
rows = None rows = None
if rows is None: if rows is None:
embed = discord.Embed(title=f"AE7Q History for {callsign}", embed.title = f"AE7Q History for {callsign}"
colour=cmn.colours.bad, embed.colour = cmn.colours.bad
url=f"{base_url}{callsign}", embed.url = f"{base_url}{callsign}"
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
embed.description = desc embed.description = desc
embed.description += f'\nNo records found for `{callsign}`' embed.description += f'\nNo records found for `{callsign}`'
await ctx.send(embed=embed) await ctx.send(embed=embed)
@@ -92,29 +90,25 @@ class AE7QCog(commands.Cog):
if len(row_cells) > 1: if len(row_cells) > 1:
table_contents += [row_cells] table_contents += [row_cells]
embed = discord.Embed(title=f"AE7Q Records for {callsign}", embed = cmn.embed_factory(ctx)
colour=cmn.colours.good, embed.title = f"AE7Q Records for {callsign}"
url=f"{base_url}{callsign}", embed.colour = cmn.colours.good
timestamp=datetime.utcnow()) embed.url = f"{base_url}{callsign}"
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
for row in table_contents[0:3]: for row in table_contents[0:3]:
header = f'**{row[0]}** ({row[1]})' header = f'**{row[0]}** ({row[1]})'
body = f'Class: *{row[2]}*\n' body = (f'Class: *{row[2]}*\n'
body += f'Region: *{row[3]}*\n' f'Region: *{row[3]}*\n'
body += f'Status: *{row[4]}*\n' f'Status: *{row[4]}*\n'
body += f'Granted: *{row[5]}*\n' f'Granted: *{row[5]}*\n'
body += f'Effective: *{row[6]}*\n' f'Effective: *{row[6]}*\n'
body += f'Cancelled: *{row[7]}*\n' f'Cancelled: *{row[7]}*\n'
body += f'Expires: *{row[8]}*' f'Expires: *{row[8]}*')
embed.add_field(name=header, value=body, inline=False) embed.add_field(name=header, value=body, inline=False)
embed.description = desc embed.description = desc
if len(table_contents) > 3: if len(table_contents) > 3:
embed.description += f'\nRecords 1 to 3 of {len(table_contents)}.' embed.description += f'\nRecords 1 to 3 of {len(table_contents)}. See ae7q.com for more...'
embed.description += ' See ae7q.com for more...'
await ctx.send(embed=embed) await ctx.send(embed=embed)
+57 -58
View File
@@ -7,7 +7,6 @@ This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
from datetime import datetime
import re import re
from collections import OrderedDict from collections import OrderedDict
import random import random
@@ -23,8 +22,7 @@ import common as cmn
class QrmHelpCommand(commands.HelpCommand): class QrmHelpCommand(commands.HelpCommand):
def __init__(self): def __init__(self):
super().__init__(command_attrs={'help': 'Shows help about qrm or a command', super().__init__(command_attrs={'help': 'Shows help about qrm or a command', 'aliases': ['h']})
'aliases': ['h']})
def get_bot_mapping(self): def get_bot_mapping(self):
bot = self.context.bot bot = self.context.bot
@@ -50,24 +48,17 @@ class QrmHelpCommand(commands.HelpCommand):
return f'{opt.prefix}{alias} {command.signature}' return f'{opt.prefix}{alias} {command.signature}'
async def send_error_message(self, error): async def send_error_message(self, error):
embed = discord.Embed(title='qrm Help Error', embed = cmn.embed_factory(self.context)
description=error, embed.title = 'qrm Help Error'
colour=cmn.colours.bad, embed.description = error
timestamp=datetime.utcnow() embed.colour = cmn.colours.bad
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
await self.context.send(embed=embed) await self.context.send(embed=embed)
async def send_bot_help(self, mapping): async def send_bot_help(self, mapping):
embed = discord.Embed(title='qrm Help', embed = cmn.embed_factory(self.context)
description=(f'For command-specific help and usage, use `{opt.prefix}help [command name]`' embed.title = 'qrm Help'
'. Many commands have shorter aliases.'), embed.description = (f'For command-specific help and usage, use `{opt.prefix}help [command name]`'
colour=cmn.colours.neutral, '. Many commands have shorter aliases.')
timestamp=datetime.utcnow()
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
for cat, cmds in mapping.items(): for cat, cmds in mapping.items():
cmds = list(filter(lambda x: not x.hidden, cmds)) cmds = list(filter(lambda x: not x.hidden, cmds))
@@ -81,23 +72,15 @@ class QrmHelpCommand(commands.HelpCommand):
await self.context.send(embed=embed) await self.context.send(embed=embed)
async def send_command_help(self, command): async def send_command_help(self, command):
embed = discord.Embed(title=self.get_command_signature(command), embed = cmn.embed_factory(self.context)
description=command.help, embed.title = self.get_command_signature(command)
colour=cmn.colours.neutral, embed.description = command.help
timestamp=datetime.utcnow()
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
await self.context.send(embed=embed) await self.context.send(embed=embed)
async def send_group_help(self, group): async def send_group_help(self, group):
embed = discord.Embed(title=self.get_command_signature(group), embed = cmn.embed_factory(self.context)
description=group.help, embed.title = self.get_command_signature(group)
colour=cmn.colours.neutral, embed.description = group.help
timestamp=datetime.utcnow()
)
embed.set_footer(text=self.context.author.name,
icon_url=str(self.context.author.avatar_url))
for cmd in group.commands: for cmd in group.commands:
embed.add_field(name=self.get_command_signature(cmd), value=cmd.help, inline=False) embed.add_field(name=self.get_command_signature(cmd), value=cmd.help, inline=False)
await self.context.send(embed=embed) await self.context.send(embed=embed)
@@ -111,42 +94,38 @@ class BaseCog(commands.Cog):
@commands.command(name="info", aliases=["about"]) @commands.command(name="info", aliases=["about"])
async def _info(self, ctx: commands.Context): async def _info(self, ctx: commands.Context):
"""Shows info about qrm.""" """Shows info about qrm."""
embed = discord.Embed(title="About qrm", embed = cmn.embed_factory(ctx)
description=info.description, embed.title = "About qrm"
colour=cmn.colours.neutral, embed.description = info.description
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
embed = embed.add_field(name="Authors", value=", ".join(info.authors)) embed.add_field(name="Authors", value=", ".join(info.authors))
embed = embed.add_field(name="License", value=info.license) embed.add_field(name="License", value=info.license)
embed = embed.add_field(name="Version", value=f'v{info.release}') embed.add_field(name="Version", value=f'v{info.release}')
embed = embed.add_field(name="Contributing", value=info.contributing, inline=False) embed.add_field(name="Contributing", value=info.contributing, inline=False)
embed.add_field(name="Official Server", value=info.bot_server, inline=False)
embed.set_thumbnail(url=str(self.bot.user.avatar_url)) embed.set_thumbnail(url=str(self.bot.user.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="ping") @commands.command(name="ping", aliases=['beep'])
async def _ping(self, ctx: commands.Context): async def _ping(self, ctx: commands.Context):
"""Show the current latency to the discord endpoint.""" """Show the current latency to the discord endpoint."""
content = ctx.message.author.mention if random.random() < 0.05 else '' embed = cmn.embed_factory(ctx)
embed = discord.Embed(title="**Pong!**", content = ''
description=f'Current ping is {self.bot.latency*1000:.1f} ms', if ctx.invoked_with == "beep":
colour=cmn.colours.neutral, embed.title = "**Boop!**"
timestamp=datetime.utcnow()) else:
embed.set_footer(text=ctx.author.name, content = ctx.message.author.mention if random.random() < 0.05 else ''
icon_url=str(ctx.author.avatar_url)) embed.title = "🏓 **Pong!**"
await ctx.send(content=content, embed=embed) embed.description = f'Current ping is {self.bot.latency*1000:.1f} ms'
await ctx.send(content, embed=embed)
@commands.command(name="changelog", aliases=["clog"]) @commands.command(name="changelog", aliases=["clog"])
async def _changelog(self, ctx: commands.Context): async def _changelog(self, ctx: commands.Context):
"""Show what has changed in the most recent bot version.""" """Show what has changed in the most recent bot version."""
embed = discord.Embed(title="qrm Changelog", embed = cmn.embed_factory(ctx)
description=("For a full listing, visit [Github](https://" embed.title = "qrm Changelog"
"github.com/classabbyamp/discord-qrm2/blob/master/CHANGELOG.md)."), embed.description = ("For a full listing, visit [Github](https://"
colour=cmn.colours.neutral, "github.com/classabbyamp/discord-qrm2/blob/master/CHANGELOG.md).")
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
changelog = self.changelog changelog = self.changelog
vers = 0 vers = 0
@@ -163,6 +142,26 @@ class BaseCog(commands.Cog):
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="issue")
async def _issue(self, ctx: commands.Context):
"""Shows how to create an issue for the bot."""
embed = cmn.embed_factory(ctx)
embed.title = "Found a bug? Have a feature request?"
embed.description = ("Submit an issue on the [issue tracker]"
"(https://github.com/classabbyamp/discord-qrm2/issues)!")
await ctx.send(embed=embed)
@commands.command(name="bruce", hidden=True)
async def _b_issue(self, ctx: commands.Context):
"""Shows how to create an issue for the bot."""
await ctx.invoke(self._issue)
@commands.command(name="echo", aliases=["e"], hidden=True)
@commands.check(cmn.check_if_owner)
async def _echo(self, ctx: commands.Context, channel: commands.TextChannelConverter, *, msg: str):
"""Send a message in a channel as qrm. Only works within a server or DM to server, not between servers."""
await channel.send(msg)
def parse_changelog(): def parse_changelog():
changelog = OrderedDict() changelog = OrderedDict()
+21
View File
@@ -7,6 +7,8 @@ This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import random
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn import common as cmn
@@ -15,6 +17,8 @@ import common as cmn
class FunCog(commands.Cog): class FunCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
with open('resources/words') as words_file:
self.words = words_file.read().lower().splitlines()
@commands.command(name="xkcd", aliases=['x'], category=cmn.cat.fun) @commands.command(name="xkcd", aliases=['x'], category=cmn.cat.fun)
async def _xkcd(self, ctx: commands.Context, number: str): async def _xkcd(self, ctx: commands.Context, number: str):
@@ -31,6 +35,23 @@ class FunCog(commands.Cog):
'''ecks dee''' '''ecks dee'''
await ctx.send('ECKS DEE :smirk:') await ctx.send('ECKS DEE :smirk:')
@commands.command(name="funetics", aliases=['fun'], category=cmn.cat.fun)
async def _funetics_lookup(self, ctx: commands.Context, *, msg: str):
'''Get fun phonetics for a word or phrase.'''
with ctx.typing():
result = ''
for char in msg.lower():
if char.isalpha():
result += random.choice([word for word in self.words if word[0] == char])
else:
result += char
result += ' '
embed = cmn.embed_factory(ctx)
embed.title = f'Funetics for {msg}'
embed.description = result.title()
embed.colour = cmn.colours.good
await ctx.send(embed=embed)
def setup(bot: commands.Bot): def setup(bot: commands.Bot):
bot.add_cog(FunCog(bot)) bot.add_cog(FunCog(bot))
+37 -52
View File
@@ -8,9 +8,7 @@ General Public License, version 2.
""" """
import math import math
from datetime import datetime
import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn import common as cmn
@@ -37,21 +35,17 @@ with negative being latitude South and longitude West.'''
grid += chr(ord('a') + int((lonf - (int(lonf/2)*2)) / (5/60))) grid += chr(ord('a') + int((lonf - (int(lonf/2)*2)) / (5/60)))
grid += chr(ord('a') + int((latf - (int(latf/1)*1)) / (2.5/60))) grid += chr(ord('a') + int((latf - (int(latf/1)*1)) / (2.5/60)))
grid += "**" grid += "**"
embed = discord.Embed(title=f'Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}', embed = cmn.embed_factory(ctx)
description=grid, embed.title = f'Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}'
colour=cmn.colours.good, embed.description = grid
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
else: else:
raise ValueError('Out of range.') raise ValueError('Out of range.')
except ValueError as err: except ValueError as err:
msg = f'Error generating grid square for {lat}, {lon}.' embed = cmn.embed_factory(ctx)
embed = discord.Embed(title=msg, description=str(err), embed.title = f'Error generating grid square for {lat}, {lon}.'
colour=cmn.colours.bad, embed.description = str(err)
timestamp=datetime.utcnow()) embed.colour = cmn.colours.bad
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="ungrid", aliases=['loc'], category=cmn.cat.maps) @commands.command(name="ungrid", aliases=['loc'], category=cmn.cat.maps)
@@ -64,28 +58,21 @@ If two grid squares are given, the distance and azimuth between them is calculat
grid = grid.upper() grid = grid.upper()
loc = get_coords(grid) loc = get_coords(grid)
if len(grid) >= 6: embed = cmn.embed_factory(ctx)
embed = discord.Embed(title=f'Latitude and Longitude for {grid}', embed.title = f'Latitude and Longitude for {grid}'
description=f'**{loc[0]:.5f}, {loc[1]:.5f}**', embed.colour = cmn.colours.good
colour=cmn.colours.good,
url=f'https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}',
timestamp=datetime.utcnow())
if len(grid) >= 6:
embed.description = f'**{loc[0]:.5f}, {loc[1]:.5f}**'
embed.url = f'https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}'
else: else:
embed = discord.Embed(title=f'Latitude and Longitude for {grid}', embed.description = f'**{loc[0]:.1f}, {loc[1]:.1f}**'
description=f'**{loc[0]:.1f}, {loc[1]:.1f}**', embed.url = f'https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}'
colour=cmn.colours.good,
url=f'https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}',
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
except Exception as e: except Exception as e:
msg = f'Error generating latitude and longitude for grid {grid}.' embed = cmn.embed_factory(ctx)
embed = discord.Embed(title=msg, description=str(e), embed.title = f'Error generating latitude and longitude for grid {grid}.'
colour=cmn.colours.bad, embed.description = str(e)
timestamp=datetime.utcnow()) embed.colour = cmn.colours.bad
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
else: else:
radius = 6371 radius = 6371
try: try:
@@ -96,34 +83,32 @@ If two grid squares are given, the distance and azimuth between them is calculat
# Haversine formula # Haversine formula
d_lat = math.radians(loc2[0] - loc[0]) d_lat = math.radians(loc2[0] - loc[0])
d_lon = math.radians(loc2[1] - loc[1]) d_lon = math.radians(loc2[1] - loc[1])
a = math.sin(d_lat/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.sin(d_lon/2) ** 2 * math.cos(math.radians(loc2[0]))
* 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 = radius * c d = radius * c
d_mi = 0.6213712 * d d_mi = 0.6213712 * d
# Bearing # Bearing
y_dist = 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_dist = math.cos(math.radians(loc[0])) * math.sin(math.radians(loc2[0])) -\ x_dist = (math.cos(math.radians(loc[0]))
math.sin(math.radians(loc[0])) * math.cos(math.radians(loc2[0])) *\ * math.sin(math.radians(loc2[0]))
math.cos(math.radians(loc2[1] - loc[1])) - math.sin(math.radians(loc[0]))
* math.cos(math.radians(loc2[0]))
* math.cos(math.radians(loc2[1] - loc[1])))
bearing = (math.degrees(math.atan2(y_dist, x_dist)) + 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}°' embed = cmn.embed_factory(ctx)
embed = discord.Embed(title=f'Great Circle Distance and Bearing from {grid} to {grid2}', embed.title = f'Great Circle Distance and Bearing from {grid} to {grid2}'
description=des, embed.description = f'**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°'
colour=cmn.colours.good, embed.colour = cmn.colours.good
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
except Exception as e: except Exception as e:
msg = f'Error generating great circle distance and bearing from {grid} and {grid2}.' embed = cmn.embed_factory(ctx)
embed = discord.Embed(title=msg, description=str(e), embed.title = f'Error generating great circle distance and bearing from {grid} and {grid2}.'
colour=cmn.colours.bad, embed.description = str(e)
timestamp=datetime.utcnow()) embed.colour = cmn.colours.bad
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
+33 -53
View File
@@ -6,59 +6,51 @@ Copyright (C) 2019 Abigail Gold, 0x5c
This file is part of discord-qrm2 and is released under the terms of the GNU This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import json
import random
from datetime import datetime from datetime import datetime
import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn import common as cmn
from resources import callsign_info from resources import callsign_info
from resources import phonetics
from resources import qcodes
class HamCog(commands.Cog): class HamCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
with open('resources/qcodes.json') as qcode_file:
self.qcodes = json.load(qcode_file)
with open('resources/words') as words_file:
self.words = words_file.read().lower().splitlines()
@commands.command(name="qcode", aliases=['q'], category=cmn.cat.ref) @commands.command(name="qcode", aliases=['q'], category=cmn.cat.ref)
async def _qcode_lookup(self, ctx: commands.Context, qcode: 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():
qcode = qcode.upper() qcode = qcode.upper()
if qcode in self.qcodes: embed = cmn.embed_factory(ctx)
embed = discord.Embed(title=qcode, description=self.qcodes[qcode], if qcode in qcodes.qcodes:
colour=cmn.colours.good, embed.title = qcode
timestamp=datetime.utcnow()) embed.description = qcodes.qcodes[qcode]
embed.colour = cmn.colours.good
else: else:
embed = discord.Embed(title=f'Q Code {qcode} not found', embed.title = f'Q Code {qcode} not found'
colour=cmn.colours.bad, embed.colour = cmn.colours.bad
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="phonetics", aliases=['ph', 'phoneticize', 'phoneticise', 'phone'], category=cmn.cat.fun) @commands.command(name="phonetics", aliases=['ph', 'phoneticize', 'phoneticise', 'phone'], category=cmn.cat.ref)
async def _phonetics_lookup(self, ctx: commands.Context, *, 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():
result += random.choice([word for word in self.words if word[0] == char]) result += phonetics.phonetics[char]
else: else:
result += char result += char
result += ' ' result += ' '
embed = discord.Embed(title=f'Phonetics for {msg}', embed = cmn.embed_factory(ctx)
description=result.title(), embed.title = f'Phonetics for {msg}'
colour=cmn.colours.good, embed.description = result.title()
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="utc", aliases=['z'], category=cmn.cat.ref) @commands.command(name="utc", aliases=['z'], category=cmn.cat.ref)
@@ -67,12 +59,10 @@ class HamCog(commands.Cog):
with ctx.typing(): with ctx.typing():
now = datetime.utcnow() now = datetime.utcnow()
result = '**' + now.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 = cmn.embed_factory(ctx)
description=result, embed.title = 'The current time is:'
colour=cmn.colours.good, embed.description = result
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"]) @commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"])
@@ -81,37 +71,27 @@ class HamCog(commands.Cog):
if country is None: if country is None:
await ctx.send_help(ctx.command) await ctx.send_help(ctx.command)
return return
embed = cmn.embed_factory(ctx)
if country.lower() not in callsign_info.options: if country.lower() not in callsign_info.options:
embed = discord.Embed(title=f'{country} not found!', embed.title = f'{country} not found!',
description=f'Valid countries: {", ".join(callsign_info.options.keys())}', embed.description = f'Valid countries: {", ".join(callsign_info.options.keys())}',
colour=cmn.colours.bad, embed.colour = cmn.colours.bad
timestamp=datetime.utcnow()) else:
embed.set_footer(text=ctx.author.name, embed.title = callsign_info.options[country.lower()][0]
icon_url=str(ctx.author.avatar_url)) embed.description = callsign_info.options[country.lower()][1]
await ctx.send(embed=embed) embed.colour = cmn.colours.good
return
embed = discord.Embed(title=callsign_info.options[country.lower()][0],
description=callsign_info.options[country.lower()][1],
colour=cmn.colours.good,
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
for name, val in callsign_info.options[country.lower()][2].items():
embed.add_field(name=name, value=val, inline=False)
for name, val in callsign_info.options[country.lower()][2].items():
embed.add_field(name=name, value=val, inline=False)
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="contests", aliases=["cc", "tests"], category=cmn.cat.ref) @commands.command(name="contests", aliases=["cc", "tests"], category=cmn.cat.ref)
async def _contests(self, ctx: commands.Context): async def _contests(self, ctx: commands.Context):
embed = discord.Embed(title="Contest Calendar", embed = cmn.embed_factory(ctx)
timestamp=datetime.utcnow(), embed.title = "Contest Calendar"
colour=cmn.colours.good)
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
embed.description = ("*We are currently rewriting the old, Chrome-based `contests` command. In the meantime, " embed.description = ("*We are currently rewriting the old, Chrome-based `contests` command. In the meantime, "
"use [the website](https://www.contestcalendar.com/weeklycont.php).*") "use [the website](https://www.contestcalendar.com/weeklycont.php).*")
embed.colour = cmn.colours.good
await ctx.send(embed=embed) await ctx.send(embed=embed)
+57 -73
View File
@@ -8,52 +8,74 @@ General Public License, version 2.
""" """
import io 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 common as cmn import common as cmn
class ImageCog(commands.Cog): class ImageCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.bandcharts = cmn.ImagesGroup(cmn.paths.bandcharts / "meta.json")
self.maps = cmn.ImagesGroup(cmn.paths.maps / "meta.json")
self.session = bot.qrm.session
@commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref) @commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref)
async def _bandplan(self, ctx: commands.Context, region: str = ''): async def _bandplan(self, ctx: commands.Context, region: str = ''):
'''Posts an image of Frequency Allocations.''' '''Posts an image of Frequency Allocations.'''
name = {'cn': 'Chinese',
'ca': 'Canadian',
'nl': 'Dutch',
'us': 'US',
'mx': 'Mexican'}
arg = region.lower() arg = region.lower()
with ctx.typing(): with ctx.typing():
if arg not in name: embed = cmn.embed_factory(ctx)
if arg not in self.bandcharts:
desc = 'Possible arguments are:\n' desc = 'Possible arguments are:\n'
for abbrev, title in name.items(): for key, img in self.bandcharts.items():
desc += f'`{abbrev}`: {title}\n' desc += f'`{key}`: {img.name}{(" " + img.emoji if img.emoji else "")}\n'
embed = discord.Embed(title=f'Bandplan Not Found!', embed.title = f'Bandplan Not Found!'
description=desc, embed.description = desc
colour=cmn.colours.bad, embed.colour = cmn.colours.bad
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
else: else:
img = discord.File(f"resources/images/bandchart/{arg}bandchart.png", metadata: cmn.ImageMetadata = self.bandcharts[arg]
filename=f'{arg}bandchart.png') img = discord.File(cmn.paths.bandcharts / metadata.filename,
embed = discord.Embed(title=f'{name[arg]} Amateur Radio Bands', filename=metadata.filename)
colour=cmn.colours.good, if metadata.description:
timestamp=datetime.utcnow()) embed.description = metadata.description
embed.set_image(url=f'attachment://{arg}bandchart.png') if metadata.source:
embed.set_footer(text=ctx.author.name, embed.add_field(name="Source", value=metadata.source)
icon_url=str(ctx.author.avatar_url)) embed.title = metadata.long_name + (" " + metadata.emoji if metadata.emoji else "")
embed.colour = cmn.colours.good
embed.set_image(url='attachment://' + metadata.filename)
await ctx.send(embed=embed, file=img)
@commands.command(name="map", category=cmn.cat.maps)
async def _map(self, ctx: commands.Context, map_id: str = ''):
'''Posts an image of a ham-relevant map.'''
arg = map_id.lower()
with ctx.typing():
embed = cmn.embed_factory(ctx)
if arg not in self.maps:
desc = 'Possible arguments are:\n'
for key, img in self.maps.items():
desc += f'`{key}`: {img.name}{(" " + img.emoji if img.emoji else "")}\n'
embed.title = 'Map Not Found!'
embed.description = desc
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
else:
metadata: cmn.ImageMetadata = self.maps[arg]
img = discord.File(cmn.paths.maps / metadata.filename,
filename=metadata.filename)
if metadata.description:
embed.description = metadata.description
if metadata.source:
embed.add_field(name="Source", value=metadata.source)
embed.title = metadata.long_name + (" " + metadata.emoji if metadata.emoji else "")
embed.colour = cmn.colours.good
embed.set_image(url='attachment://' + metadata.filename)
await ctx.send(embed=embed, file=img) await ctx.send(embed=embed, file=img)
@commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl'], category=cmn.cat.maps) @commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl'], category=cmn.cat.maps)
@@ -62,56 +84,18 @@ class ImageCog(commands.Cog):
gl_url = ('http://www.fourmilab.ch/cgi-bin/uncgi/Earth?img=NOAAtopo.evif' 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=') '&imgsize=320&dynimg=y&opt=-p&lat=&lon=&alt=&tle=&date=0&utc=&jd=')
with ctx.typing(): with ctx.typing():
embed = discord.Embed(title='Current Greyline Conditions', embed = cmn.embed_factory(ctx)
colour=cmn.colours.good, embed.title = 'Current Greyline Conditions'
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
async with aiohttp.ClientSession() as session: async with self.session.get(gl_url) as resp:
async with session.get(gl_url) as resp: if resp.status != 200:
if resp.status != 200: embed.description = 'Could not download file...'
embed.description = 'Could not download file...' embed.colour = cmn.colours.bad
embed.colour = cmn.colours.bad else:
else: data = io.BytesIO(await resp.read())
data = io.BytesIO(await resp.read()) embed.set_image(url=f'attachment://greyline.jpg')
embed.set_image(url=f'attachment://greyline.jpg')
embed.set_footer(text=ctx.author.name,
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", category=cmn.cat.maps)
async def _map(self, ctx: commands.Context, map_id: str = ''):
'''Posts an image of a ham-relevant map.'''
map_titles = {"cq": 'Worldwide CQ Zones Map',
"itu": 'Worldwide ITU Zones Map',
"arrl": 'ARRL/RAC Section Map',
"rac": 'ARRL/RAC Section Map',
"cn": 'Chinese Callsign Areas',
"us": 'US Callsign Areas'}
arg = map_id.lower()
with ctx.typing():
if arg not in map_titles:
desc = 'Possible arguments are:\n'
for abbrev, title in map_titles.items():
desc += f'`{abbrev}`: {title}\n'
embed = discord.Embed(title=f'Map Not Found!',
description=desc,
colour=cmn.colours.bad,
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed)
else:
img = discord.File(f"resources/images/map/{arg}map.png",
filename=f'{arg}map.png')
embed = discord.Embed(title=f'{map_titles[arg]} Map',
colour=cmn.colours.good,
timestamp=datetime.utcnow())
embed.set_image(url=f'attachment://{arg}map.png')
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=img)
def setup(bot: commands.Bot): def setup(bot: commands.Bot):
bot.add_cog(ImageCog(bot)) bot.add_cog(ImageCog(bot))
+7 -15
View File
@@ -7,10 +7,8 @@ This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
from datetime import datetime
import threading import threading
import discord
from discord.ext import commands, tasks from discord.ext import commands, tasks
from ctyparser import BigCty from ctyparser import BigCty
@@ -43,24 +41,18 @@ class LookupCog(commands.Cog):
with ctx.typing(): with ctx.typing():
query = query.upper() query = query.upper()
full_query = query full_query = query
embed = discord.Embed(title=f'DXCC Info for ', embed = cmn.embed_factory(ctx)
timestamp=datetime.utcnow()) embed.title = f'DXCC Info for '
embed.set_footer(text=f'{ctx.author.name}',
icon_url=str(ctx.author.avatar_url))
embed.description = f'*Last Updated: {self.cty.formatted_version}*' embed.description = f'*Last Updated: {self.cty.formatted_version}*'
embed.colour = cmn.colours.bad embed.colour = cmn.colours.bad
while query: while query:
if query in self.cty.keys(): if query in self.cty.keys():
data = self.cty[query] data = self.cty[query]
embed.add_field(name="Entity", embed.add_field(name="Entity", value=data['entity'])
value=data['entity'])\ embed.add_field(name="CQ Zone", value=data['cq'])
.add_field(name="CQ Zone", embed.add_field(name="ITU Zone", value=data['itu'])
value=data['cq'])\ embed.add_field(name="Continent", value=data['continent'])
.add_field(name="ITU Zone", embed.add_field(name="Time Zone",
value=data['itu'])\
.add_field(name="Continent",
value=data['continent'])\
.add_field(name="Time Zone",
value=f'+{data["tz"]}' if data['tz'] > 0 else str(data['tz'])) value=f'+{data["tz"]}' if data['tz'] > 0 else str(data['tz']))
embed.title += query embed.title += query
embed.colour = cmn.colours.good embed.colour = cmn.colours.good
+20 -31
View File
@@ -7,21 +7,15 @@ This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
import json
from datetime import datetime
import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import common as cmn import common as cmn
from resources import morse
class MorseCog(commands.Cog): class MorseCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
with open('resources/morse.json') as morse_file:
self.ascii2morse = json.load(morse_file)
self.morse2ascii = {v: k for k, v in self.ascii2morse.items()}
@commands.command(name="morse", aliases=['cw'], category=cmn.cat.ref) @commands.command(name="morse", aliases=['cw'], category=cmn.cat.ref)
async def _morse(self, ctx: commands.Context, *, msg: str): async def _morse(self, ctx: commands.Context, *, msg: str):
@@ -30,16 +24,14 @@ class MorseCog(commands.Cog):
result = '' result = ''
for char in msg.upper(): for char in msg.upper():
try: try:
result += self.ascii2morse[char] result += morse.morse[char]
except KeyError: except KeyError:
result += '<?>' result += '<?>'
result += ' ' result += ' '
embed = discord.Embed(title=f'Morse Code for {msg}', embed = cmn.embed_factory(ctx)
description=result, embed.title = f'Morse Code for {msg}'
colour=cmn.colours.good, embed.description = '**' + result + '**'
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="unmorse", aliases=['demorse', 'uncw', 'decw'], category=cmn.cat.ref) @commands.command(name="unmorse", aliases=['demorse', 'uncw', 'decw'], category=cmn.cat.ref)
@@ -53,39 +45,36 @@ class MorseCog(commands.Cog):
for word in msg: for word in msg:
for char in word: for char in word:
try: try:
result += self.morse2ascii[char] result += morse.ascii[char]
except KeyError: except KeyError:
result += '<?>' result += '<?>'
result += ' ' result += ' '
embed = discord.Embed(title=f'ASCII for {msg0}', embed = cmn.embed_factory(ctx)
description=result, embed.title = f'ASCII for {msg0}'
colour=cmn.colours.good, embed.description = result
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
@commands.command(name="cwweight", aliases=["weight", 'cww'], category=cmn.cat.ref) @commands.command(name="cwweight", aliases=["weight", 'cww'], category=cmn.cat.ref)
async def _weight(self, ctx: commands.Context, *, msg: str): async def _weight(self, ctx: commands.Context, *, msg: str):
'''Calculates the CW Weight of a callsign or message.''' '''Calculates the CW Weight of a callsign or message.'''
embed = cmn.embed_factory(ctx)
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:
cw_char = self.ascii2morse[char].replace('-', '==') cw_char = morse.morse[char].replace('-', '==')
weight += len(cw_char) * 2 + 2 weight += len(cw_char) * 2 + 2
except KeyError: except KeyError:
res = f'Unknown character {char} in callsign' embed.title = 'Error in calculation of CW weight'
await ctx.send(res) embed.description = f'Unknown character {char} in callsign'
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
return return
res = f'The CW weight is **{weight}**' embed.title = f'CW Weight of {msg}'
embed = discord.Embed(title=f'CW Weight of {msg}', embed.description = f'The CW weight is **{weight}**'
description=res, embed.colour = cmn.colours.good
colour=cmn.colours.good,
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
+24 -30
View File
@@ -7,10 +7,8 @@ This file is part of discord-qrm2 and is released under the terms of the GNU
General Public License, version 2. General Public License, version 2.
""" """
from collections import OrderedDict from collections import OrderedDict
from datetime import datetime
from io import BytesIO from io import BytesIO
import discord
from discord.ext import commands, tasks from discord.ext import commands, tasks
import aiohttp import aiohttp
@@ -23,7 +21,7 @@ import data.keys as keys
class QRZCog(commands.Cog): class QRZCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.session = aiohttp.ClientSession() self.session = bot.qrm.session
self._qrz_session_init.start() self._qrz_session_init.start()
@commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup) @commands.command(name="call", aliases=["qrz"], category=cmn.cat.lookup)
@@ -42,10 +40,10 @@ class QRZCog(commands.Cog):
async with self.session.get(url) as resp: async with self.session.get(url) as resp:
if resp.status != 200: if resp.status != 200:
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})') raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
resp_xml = etree.parse(BytesIO(await resp.read())).getroot() with BytesIO(await resp.read()) as resp_file:
resp_xml = etree.parse(resp_file).getroot()
resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session', resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session', namespaces={'x': 'http://xmldata.qrz.com'})
namespaces={'x': 'http://xmldata.qrz.com'})
resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()} resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()}
if 'Error' in resp_session: if 'Error' in resp_session:
if 'Session Timeout' in resp_session['Error']: if 'Session Timeout' in resp_session['Error']:
@@ -53,26 +51,21 @@ class QRZCog(commands.Cog):
await self._qrz_lookup(ctx, callsign) await self._qrz_lookup(ctx, callsign)
return return
if 'Not found' in resp_session['Error']: if 'Not found' in resp_session['Error']:
embed = discord.Embed(title=f"QRZ Data for {callsign.upper()}", embed = cmn.embed_factory(ctx)
colour=cmn.colours.bad, embed.title = f"QRZ Data for {callsign.upper()}"
description='No data found!', embed.colour = cmn.colours.bad
timestamp=datetime.utcnow()) embed.description = 'No data found!'
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
return return
raise ValueError(resp_session['Error']) raise ValueError(resp_session['Error'])
resp_xml_data = resp_xml.xpath('/x:QRZDatabase/x:Callsign', resp_xml_data = resp_xml.xpath('/x:QRZDatabase/x:Callsign', namespaces={'x': 'http://xmldata.qrz.com'})
namespaces={'x': 'http://xmldata.qrz.com'})
resp_data = {el.tag.split('}')[1]: el.text for el in resp_xml_data[0].getiterator()} resp_data = {el.tag.split('}')[1]: el.text for el in resp_xml_data[0].getiterator()}
embed = discord.Embed(title=f"QRZ Data for {resp_data['call']}", embed = cmn.embed_factory(ctx)
colour=cmn.colours.good, embed.title = f"QRZ Data for {resp_data['call']}"
url=f'http://www.qrz.com/db/{resp_data["call"]}', embed.colour = cmn.colours.good
timestamp=datetime.utcnow()) embed.url = f'http://www.qrz.com/db/{resp_data["call"]}'
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
if 'image' in resp_data: if 'image' in resp_data:
embed.set_thumbnail(url=resp_data['image']) embed.set_thumbnail(url=resp_data['image'])
@@ -105,10 +98,10 @@ async def qrz_login(user: str, passwd: str, session: aiohttp.ClientSession):
async with session.get(url) as resp: async with session.get(url) as resp:
if resp.status != 200: if resp.status != 200:
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})') raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
resp_xml = etree.parse(BytesIO(await resp.read())).getroot() with BytesIO(await resp.read()) as resp_file:
resp_xml = etree.parse(resp_file).getroot()
resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session', resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session', namespaces={'x': 'http://xmldata.qrz.com'})
namespaces={'x': 'http://xmldata.qrz.com'})
resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()} resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()}
if 'Error' in resp_session: if 'Error' in resp_session:
raise ConnectionError(resp_session['Error']) raise ConnectionError(resp_session['Error'])
@@ -122,10 +115,10 @@ async def qrz_test_session(key: str, session: aiohttp.ClientSession):
async with session.get(url) as resp: async with session.get(url) as resp:
if resp.status != 200: if resp.status != 200:
raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})') raise ConnectionError(f'Unable to connect to QRZ (HTTP Error {resp.status})')
resp_xml = etree.parse(BytesIO(await resp.read())).getroot() with BytesIO(await resp.read()) as resp_file:
resp_xml = etree.parse(resp_file).getroot()
resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session', resp_xml_session = resp_xml.xpath('/x:QRZDatabase/x:Session', namespaces={'x': 'http://xmldata.qrz.com'})
namespaces={'x': 'http://xmldata.qrz.com'})
resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()} resp_session = {el.tag.split('}')[1]: el.text for el in resp_xml_session[0].getiterator()}
if 'Error' in resp_session: if 'Error' in resp_session:
raise ConnectionError(resp_session['Error']) raise ConnectionError(resp_session['Error'])
@@ -143,8 +136,10 @@ def qrz_process_info(data: dict):
state = f', {data["state"]}' state = f', {data["state"]}'
else: else:
state = '' state = ''
address = data.get('addr1', '') + '\n' + data.get('addr2', '') + \ address = data.get('addr1', '') + '\n' + data.get('addr2', '') + state + ' ' + data.get('zip', '')
state + ' ' + data.get('zip', '') address = address.strip()
if address == '':
address = None
if 'eqsl' in data: if 'eqsl' in data:
eqsl = 'Yes' if data['eqsl'] == 1 else 'No' eqsl = 'Yes' if data['eqsl'] == 1 else 'No'
else: else:
@@ -177,8 +172,7 @@ def qrz_process_info(data: dict):
('CQ Zone', data.get('cqzone', None)), ('CQ Zone', data.get('cqzone', None)),
('ITU Zone', data.get('ituzone', None)), ('ITU Zone', data.get('ituzone', None)),
('IOTA Designator', data.get('iota', None)), ('IOTA Designator', data.get('iota', None)),
('Born', data.get('born', None)), ('Born', data.get('born', None))])
])
def setup(bot): def setup(bot):
+36 -41
View File
@@ -9,13 +9,9 @@ General Public License, version 2.
import random import random
import json import json
from datetime import datetime
import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import aiohttp
import common as cmn import common as cmn
@@ -24,6 +20,7 @@ class StudyCog(commands.Cog):
self.bot = bot self.bot = bot
self.lastq = dict() self.lastq = dict()
self.source = 'Data courtesy of [HamStudy.org](https://hamstudy.org/)' self.source = 'Data courtesy of [HamStudy.org](https://hamstudy.org/)'
self.session = bot.qrm.session
@commands.command(name="hamstudy", aliases=['rq', 'randomquestion', 'randomq'], category=cmn.cat.study) @commands.command(name="hamstudy", aliases=['rq', 'randomquestion', 'randomq'], category=cmn.cat.study)
async def _random_question(self, ctx: commands.Context, level: str = None): async def _random_question(self, ctx: commands.Context, level: str = None):
@@ -32,6 +29,7 @@ class StudyCog(commands.Cog):
gen_pool = 'E3_2019' gen_pool = 'E3_2019'
extra_pool = 'E4_2016' extra_pool = 'E4_2016'
embed = cmn.embed_factory(ctx)
with ctx.typing(): with ctx.typing():
selected_pool = None selected_pool = None
try: try:
@@ -51,38 +49,40 @@ class StudyCog(commands.Cog):
if (level is None) or (level == 'all'): # no pool given or user wants all, so pick a random pool 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. ' + embed.title = 'Error in HamStudy command'
'There are many ways to call up certain question pools - try ?rq t, g, or e. ' + embed.description = ('The question pool you gave was unrecognized. '
'(Note that only the US question pools are available).') 'There are many ways to call up certain question pools - try ?rq t, g, or e. '
'\n\nNote that currently only the US question pools are available.')
embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
return return
async with aiohttp.ClientSession() as session: async with self.session.get(f'https://hamstudy.org/pools/{selected_pool}') as resp:
async with session.get(f'https://hamstudy.org/pools/{selected_pool}') as resp: if resp.status != 200:
if resp.status != 200: embed.title = 'Error in HamStudy command'
return await ctx.send('Could not load questions...') embed.description = 'Could not load questions'
pool = json.loads(await resp.read())['pool'] embed.colour = cmn.colours.bad
await ctx.send(embed=embed)
return
pool = json.loads(await resp.read())['pool']
# Select a question # Select a question
pool_section = random.choice(pool)['sections'] pool_section = random.choice(pool)['sections']
pool_questions = random.choice(pool_section)['questions'] pool_questions = random.choice(pool_section)['questions']
question = random.choice(pool_questions) question = random.choice(pool_questions)
embed = discord.Embed(title=question['id'], embed.title = question['id']
description=self.source, embed.description = self.source
colour=cmn.colours.good, embed.colour = cmn.colours.good
timestamp=datetime.utcnow()) embed.add_field(name='Question:', value=question['text'], inline=False)
embed.set_footer(text=ctx.author.name, embed.add_field(name='Answers:', value='**A:** ' + question['answers']['A']
icon_url=str(ctx.author.avatar_url)) + '\n**B:** ' + question['answers']['B']
embed = embed.add_field(name='Question:', value=question['text'], inline=False) + '\n**C:** ' + question['answers']['C']
embed = embed.add_field(name='Answers:', value='**A:** ' + question['answers']['A'] + + '\n**D:** ' + question['answers']['D'], inline=False)
'\n**B:** ' + question['answers']['B'] + embed.add_field(name='Answer:', value='Type _?rqa_ for answer', inline=False)
'\n**C:** ' + question['answers']['C'] +
'\n**D:** ' + question['answers']['D'],
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"]}'
embed = embed.set_image(url=image_url) embed.set_image(url=image_url)
self.lastq[ctx.message.channel.id] = (question['id'], question['answer']) self.lastq[ctx.message.channel.id] = (question['id'], question['answer'])
await ctx.send(embed=embed) await ctx.send(embed=embed)
@@ -93,29 +93,24 @@ class StudyCog(commands.Cog):
with ctx.typing(): with ctx.typing():
correct_ans = self.lastq[ctx.message.channel.id][1] correct_ans = self.lastq[ctx.message.channel.id][1]
q_num = self.lastq[ctx.message.channel.id][0] q_num = self.lastq[ctx.message.channel.id][0]
embed = cmn.embed_factory(ctx)
if answer is not None: if answer is not None:
answer = answer.upper() answer = answer.upper()
if answer == correct_ans: if answer == correct_ans:
result = f'Correct! The answer to {q_num} was **{correct_ans}**.' result = f'Correct! The answer to {q_num} was **{correct_ans}**.'
embed = discord.Embed(title=f'{q_num} Answer', embed.title = f'{q_num} Answer'
description=f'{self.source}\n\n{result}', embed.description = f'{self.source}\n\n{result}'
colour=cmn.colours.good, embed.colour = cmn.colours.good
timestamp=datetime.utcnow())
else: else:
result = f'Incorrect. The answer to {q_num} was **{correct_ans}**, not **{answer}**.' result = f'Incorrect. The answer to {q_num} was **{correct_ans}**, not **{answer}**.'
embed = discord.Embed(title=f'{q_num} Answer', embed.title = f'{q_num} Answer'
description=f'{self.source}\n\n{result}', embed.description = f'{self.source}\n\n{result}'
colour=cmn.colours.bad, embed.colour = cmn.colours.bad
timestamp=datetime.utcnow())
else: else:
result = f'The correct answer to {q_num} was **{correct_ans}**.' result = f'The correct answer to {q_num} was **{correct_ans}**.'
embed = discord.Embed(title=f'{q_num} Answer', embed.title = f'{q_num} Answer'
description=f'{self.source}\n\n{result}', embed.description = f'{self.source}\n\n{result}'
colour=cmn.colours.neutral, embed.colour = cmn.colours.neutral
timestamp=datetime.utcnow())
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed) await ctx.send(embed=embed)
+36 -45
View File
@@ -8,14 +8,11 @@ General Public License, version 2.
""" """
import io import io
from datetime import datetime
import re import re
import discord import discord
import discord.ext.commands as commands import discord.ext.commands as commands
import aiohttp
import common as cmn import common as cmn
@@ -24,24 +21,22 @@ class WeatherCog(commands.Cog):
def __init__(self, bot: commands.Bot): def __init__(self, bot: commands.Bot):
self.bot = bot self.bot = bot
self.session = bot.qrm.session
@commands.command(name="bandconditions", aliases=['cond', 'condx', 'conditions'], category=cmn.cat.weather) @commands.command(name="bandconditions", aliases=['cond', 'condx', 'conditions'], category=cmn.cat.weather)
async def _band_conditions(self, ctx: commands.Context): 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():
embed = discord.Embed(title='Current Solar Conditions', embed = cmn.embed_factory(ctx)
colour=cmn.colours.good, embed.title = 'Current Solar Conditions'
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
async with aiohttp.ClientSession() as session: async with self.session.get('http://www.hamqsl.com/solarsun.php') as resp:
async with session.get('http://www.hamqsl.com/solarsun.php') as resp: if resp.status != 200:
if resp.status != 200: embed.description = 'Could not download file...'
embed.description = 'Could not download file...' embed.colour = cmn.colours.bad
embed.colour = cmn.colours.bad else:
else: data = io.BytesIO(await resp.read())
data = io.BytesIO(await resp.read()) embed.set_image(url=f'attachment://condx.png')
embed.set_image(url=f'attachment://condx.png')
embed.set_footer(text=ctx.author.name,
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.group(name="weather", aliases=['wttr'], category=cmn.cat.weather) @commands.group(name="weather", aliases=['wttr'], category=cmn.cat.weather)
@@ -78,22 +73,20 @@ See help for weather command for possible location types. Add a `-c` or `-f` to
loc = self.wttr_units_regex.sub('', location).strip() loc = self.wttr_units_regex.sub('', location).strip()
embed = discord.Embed(title=f'Weather Forecast for {loc}', embed = cmn.embed_factory(ctx)
description='Data from [wttr.in](http://wttr.in/).', embed.title = f'Weather Forecast for {loc}'
colour=cmn.colours.good, embed.description = 'Data from [wttr.in](http://wttr.in/).'
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
loc = loc.replace(' ', '+') loc = loc.replace(' ', '+')
async with aiohttp.ClientSession() as session: async with self.session.get(f'http://wttr.in/{loc}_{units}pnFQ.png') as resp:
async with session.get(f'http://wttr.in/{loc}_{units}pnFQ.png') as resp: if resp.status != 200:
if resp.status != 200: embed.description = 'Could not download file...'
embed.description = 'Could not download file...' embed.colour = cmn.colours.bad
embed.colour = cmn.colours.bad else:
else: data = io.BytesIO(await resp.read())
data = io.BytesIO(await resp.read()) embed.set_image(url=f'attachment://wttr_forecast.png')
embed.set_image(url=f'attachment://wttr_forecast.png') await ctx.send(embed=embed, file=discord.File(data, 'wttr_forecast.png'))
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, f'wttr_forecast.png'))
@_weather_conditions.command(name='now', aliases=['n'], category=cmn.cat.weather) @_weather_conditions.command(name='now', aliases=['n'], category=cmn.cat.weather)
async def _weather_conditions_now(self, ctx: commands.Context, *, location: str): async def _weather_conditions_now(self, ctx: commands.Context, *, location: str):
@@ -113,21 +106,19 @@ See help for weather command for possible location types. Add a `-c` or `-f` to
loc = self.wttr_units_regex.sub('', location).strip() loc = self.wttr_units_regex.sub('', location).strip()
embed = discord.Embed(title=f'Current Weather for {loc}', embed = cmn.embed_factory(ctx)
description='Data from [wttr.in](http://wttr.in/).', embed.title = f'Current Weather for {loc}'
colour=cmn.colours.good, embed.description = 'Data from [wttr.in](http://wttr.in/).'
timestamp=datetime.utcnow()) embed.colour = cmn.colours.good
loc = loc.replace(' ', '+') loc = loc.replace(' ', '+')
async with aiohttp.ClientSession() as session: async with self.session.get(f'http://wttr.in/{loc}_0{units}pnFQ.png') as resp:
async with session.get(f'http://wttr.in/{loc}_0{units}pnFQ.png') as resp: if resp.status != 200:
if resp.status != 200: embed.description = 'Could not download file...'
embed.description = 'Could not download file...' embed.colour = cmn.colours.bad
embed.colour = cmn.colours.bad else:
else: data = io.BytesIO(await resp.read())
data = io.BytesIO(await resp.read()) embed.set_image(url=f'attachment://wttr_now.png')
embed.set_image(url=f'attachment://wttr_now.png')
embed.set_footer(text=ctx.author.name,
icon_url=str(ctx.author.avatar_url))
await ctx.send(embed=embed, file=discord.File(data, 'wttr_now.png')) await ctx.send(embed=embed, file=discord.File(data, 'wttr_now.png'))
+2 -1
View File
@@ -24,4 +24,5 @@ authors = ("@ClassAbbyAmplifier#2229", "@0x5c#0639")
description = """A bot with various useful ham radio-related functions, written in Python.""" description = """A bot with various useful ham radio-related functions, written in Python."""
license = "Released under the GNU General Public License v2" license = "Released under the GNU General Public License v2"
contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm2" contributing = "Check out the source on GitHub, contributions welcome: https://github.com/classabbyamp/discord-qrm2"
release = '2.0.0' release = '2.1.0'
bot_server = 'https://discord.gg/Ntbg3J4'
+110 -24
View File
@@ -9,14 +9,20 @@ General Public License, version 2.
""" """
from datetime import datetime import sys
import traceback
from datetime import time, datetime
import random
from types import SimpleNamespace
import pytz
import aiohttp
import discord import discord
from discord.ext import commands, tasks from discord.ext import commands, tasks
import common as cmn import common as cmn
import info import info
import data.options as opt import data.options as opt
import data.keys as keys import data.keys as keys
@@ -36,6 +42,11 @@ bot = commands.Bot(command_prefix=opt.prefix,
description=info.description, description=info.description,
help_command=commands.MinimalHelpCommand()) help_command=commands.MinimalHelpCommand())
bot.qrm = SimpleNamespace()
bot.qrm.session = aiohttp.ClientSession(headers={'User-Agent': f'discord-qrm2/{info.release}'})
bot.qrm.debug_mode = debug_mode
# --- Commands --- # --- Commands ---
@@ -43,8 +54,9 @@ bot = commands.Bot(command_prefix=opt.prefix,
@commands.check(cmn.check_if_owner) @commands.check(cmn.check_if_owner)
async def _restart_bot(ctx: commands.Context): async def _restart_bot(ctx: commands.Context):
"""Restarts the bot.""" """Restarts the bot."""
await bot.qrm.session.close()
global exit_code global exit_code
await cmn.add_react(ctx.message, cmn.emojis.good) await cmn.add_react(ctx.message, cmn.emojis.check_mark)
print(f"[**] Restarting! Requested by {ctx.author}.") print(f"[**] Restarting! Requested by {ctx.author}.")
exit_code = 42 # Signals to the wrapper script that the bot needs to be restarted. exit_code = 42 # Signals to the wrapper script that the bot needs to be restarted.
await bot.logout() await bot.logout()
@@ -54,8 +66,9 @@ async def _restart_bot(ctx: commands.Context):
@commands.check(cmn.check_if_owner) @commands.check(cmn.check_if_owner)
async def _shutdown_bot(ctx: commands.Context): async def _shutdown_bot(ctx: commands.Context):
"""Shuts down the bot.""" """Shuts down the bot."""
await bot.qrm.session.close()
global exit_code global exit_code
await cmn.add_react(ctx.message, cmn.emojis.good) await cmn.add_react(ctx.message, cmn.emojis.check_mark)
print(f"[**] Shutting down! Requested by {ctx.author}.") print(f"[**] Shutting down! Requested by {ctx.author}.")
exit_code = 0 # Signals to the wrapper script that the bot should not be restarted. exit_code = 0 # Signals to the wrapper script that the bot should not be restarted.
await bot.logout() await bot.logout()
@@ -74,9 +87,8 @@ async def _extctl(ctx: commands.Context):
@_extctl.command(name="list") @_extctl.command(name="list")
async def _extctl_list(ctx: commands.Context): async def _extctl_list(ctx: commands.Context):
"""Lists Extensions.""" """Lists Extensions."""
embed = discord.Embed(title="Loaded Extensions", embed = cmn.embed_factory(ctx)
colour=cmn.colours.neutral, embed.title = "Loaded Extensions"
timestamp=datetime.utcnow())
embed.description = "\n".join(["" + x.split(".")[1] for x in bot.extensions.keys()]) embed.description = "\n".join(["" + x.split(".")[1] for x in bot.extensions.keys()])
await ctx.send(embed=embed) await ctx.send(embed=embed)
@@ -85,19 +97,23 @@ async def _extctl_list(ctx: commands.Context):
async def _extctl_load(ctx: commands.Context, extension: str): async def _extctl_load(ctx: commands.Context, extension: str):
try: try:
bot.load_extension(ext_dir + "." + extension) bot.load_extension(ext_dir + "." + extension)
await cmn.add_react(ctx.message, cmn.emojis.good) await cmn.add_react(ctx.message, cmn.emojis.check_mark)
except commands.ExtensionError as ex: except commands.ExtensionError as ex:
embed = cmn.error_embed_factory(ctx, ex, debug_mode) embed = cmn.error_embed_factory(ctx, ex, bot.qrm.debug_mode)
await ctx.send(embed=embed) await ctx.send(embed=embed)
@_extctl.command(name="reload") @_extctl.command(name="reload", aliases=["relaod"])
async def _extctl_reload(ctx: commands.Context, extension: str): async def _extctl_reload(ctx: commands.Context, extension: str):
if ctx.invoked_with == "relaod":
pika = bot.get_emoji(opt.pika)
if pika:
await cmn.add_react(ctx.message, pika)
try: try:
bot.reload_extension(ext_dir + "." + extension) bot.reload_extension(ext_dir + "." + extension)
await cmn.add_react(ctx.message, cmn.emojis.good) await cmn.add_react(ctx.message, cmn.emojis.check_mark)
except commands.ExtensionError as ex: except commands.ExtensionError as ex:
embed = cmn.error_embed_factory(ctx, ex, debug_mode) embed = cmn.error_embed_factory(ctx, ex, bot.qrm.debug_mode)
await ctx.send(embed=embed) await ctx.send(embed=embed)
@@ -105,9 +121,9 @@ async def _extctl_reload(ctx: commands.Context, extension: str):
async def _extctl_unload(ctx: commands.Context, extension: str): async def _extctl_unload(ctx: commands.Context, extension: str):
try: try:
bot.unload_extension(ext_dir + "." + extension) bot.unload_extension(ext_dir + "." + extension)
await cmn.add_react(ctx.message, cmn.emojis.good) await cmn.add_react(ctx.message, cmn.emojis.check_mark)
except commands.ExtensionError as ex: except commands.ExtensionError as ex:
embed = cmn.error_embed_factory(ctx, ex, debug_mode) embed = cmn.error_embed_factory(ctx, ex, bot.qrm.debug_mode)
await ctx.send(embed=embed) await ctx.send(embed=embed)
@@ -117,18 +133,90 @@ async def _extctl_unload(ctx: commands.Context, extension: str):
async def on_ready(): async def on_ready():
print(f"Logged in as: {bot.user} - {bot.user.id}") print(f"Logged in as: {bot.user} - {bot.user.id}")
print("------") print("------")
if opt.status_mode == "time":
_ensure_activity_time.start()
elif opt.status_mode == "random":
_ensure_activity_random.start()
else:
_ensure_activity_fixed.start()
@bot.event
async def on_message(message):
msg = message.content.lower()
for emoji, keywords in opt.msg_reacts.items():
if any([keyword in msg for keyword in keywords]):
await message.add_reaction(discord.utils.find(lambda x: x.id == emoji, bot.emojis))
await bot.process_commands(message)
@bot.event
async def on_command_error(ctx: commands.Context, err: commands.CommandError):
if isinstance(err, commands.UserInputError):
await cmn.add_react(ctx.message, cmn.emojis.warning)
await ctx.send_help(ctx.command)
elif isinstance(err, commands.CommandNotFound) and not ctx.invoked_with.startswith("?"):
await cmn.add_react(ctx.message, cmn.emojis.question)
elif isinstance(err, commands.CheckFailure):
# Add handling of other subclasses of CheckFailure as needed.
if isinstance(err, commands.NotOwner):
await cmn.add_react(ctx.message, cmn.emojis.no_entry)
else:
await cmn.add_react(ctx.message, cmn.emojis.x)
elif isinstance(err, commands.DisabledCommand):
await cmn.add_react(ctx.message, cmn.emojis.bangbang)
elif isinstance(err, (commands.CommandInvokeError, commands.ConversionError)):
# Emulating discord.py's default beaviour.
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
traceback.print_exception(type(err), err, err.__traceback__, file=sys.stderr)
embed = cmn.error_embed_factory(ctx, err.original, bot.qrm.debug_mode)
embed.description += f"\n`{type(err).__name__}`"
await cmn.add_react(ctx.message, cmn.emojis.warning)
await ctx.send(embed=embed)
else:
# Emulating discord.py's default beaviour. (safest bet)
print('Ignoring exception in command {}:'.format(ctx.command), file=sys.stderr)
traceback.print_exception(type(err), err, err.__traceback__, file=sys.stderr)
await cmn.add_react(ctx.message, cmn.emojis.warning)
# --- Tasks --- # --- Tasks ---
@tasks.loop(minutes=5) @tasks.loop(minutes=5)
async def _ensure_activity(): async def _ensure_activity_time():
await bot.change_presence(activity=discord.Game(name=opt.game)) status = opt.statuses[0]
try:
tz = pytz.timezone(opt.status_tz)
except pytz.exceptions.UnknownTimeZoneError:
await bot.change_presence(activity=discord.Game(name="with invalid timezones."))
return
now = datetime.now(tz=tz).time()
for sts in opt.time_statuses:
start_time = time(hour=sts[1][0], minute=sts[1][1], tzinfo=tz)
end_time = time(hour=sts[2][0], minute=sts[2][1], tzinfo=tz)
if start_time < now <= end_time:
status = sts[0]
await bot.change_presence(activity=discord.Game(name=status))
@_ensure_activity.before_loop @tasks.loop(minutes=5)
async def _before_ensure_activity(): async def _ensure_activity_random():
await bot.wait_until_ready() status = random.choice(opt.statuses)
await bot.change_presence(activity=discord.Game(name=status))
@tasks.loop(minutes=5)
async def _ensure_activity_fixed():
status = opt.statuses[0]
await bot.change_presence(activity=discord.Game(name=status))
# --- Run --- # --- Run ---
@@ -136,27 +224,25 @@ async def _before_ensure_activity():
for ext in opt.exts: for ext in opt.exts:
bot.load_extension(ext_dir + '.' + ext) bot.load_extension(ext_dir + '.' + ext)
_ensure_activity.start()
try: try:
bot.run(keys.discord_token) bot.run(keys.discord_token)
except discord.LoginFailure as ex: except discord.LoginFailure as ex:
# Miscellaneous authentications errors: borked token and co # Miscellaneous authentications errors: borked token and co
if debug_mode: if bot.qrm.debug_mode:
raise raise
raise SystemExit("Error: Failed to authenticate: {}".format(ex)) raise SystemExit("Error: Failed to authenticate: {}".format(ex))
except discord.ConnectionClosed as ex: except discord.ConnectionClosed as ex:
# When the connection to the gateway (websocket) is closed # When the connection to the gateway (websocket) is closed
if debug_mode: if bot.qrm.debug_mode:
raise raise
raise SystemExit("Error: Discord gateway connection closed: [Code {}] {}".format(ex.code, ex.reason)) raise SystemExit("Error: Discord gateway connection closed: [Code {}] {}".format(ex.code, ex.reason))
except ConnectionResetError as ex: except ConnectionResetError as ex:
# More generic connection reset error # More generic connection reset error
if debug_mode: if bot.qrm.debug_mode:
raise raise
raise SystemExit("ConnectionResetError: {}".format(ex)) raise SystemExit("ConnectionResetError: {}".format(ex))
+1
View File
@@ -2,3 +2,4 @@ discord.py
ctyparser ctyparser
beautifulsoup4 beautifulsoup4
lxml lxml
pytz
+8 -10
View File
@@ -11,7 +11,7 @@ from collections import OrderedDict
us_calls_title = "Valid US Vanity Callsigns" us_calls_title = "Valid US Vanity Callsigns"
us_calls_desc = ('#x# is the number of letters in the prefix and suffix of a callsign.' us_calls_desc = ('#x# is the number of letters in the prefix and suffix of a callsign. '
'E.g., WY4RC would be a 2x2 callsign, with prefix WY and suffix RC.') 'E.g., WY4RC would be a 2x2 callsign, with prefix WY and suffix RC.')
us_calls = OrderedDict([('**Group A** (Extra Only)', ('**Any:** K, N, W (1x2)\n' us_calls = OrderedDict([('**Group A** (Extra Only)', ('**Any:** K, N, W (1x2)\n'
' AA-AL, KA-KZ, NA-NZ, WA-WZ (2x1)\n' ' AA-AL, KA-KZ, NA-NZ, WA-WZ (2x1)\n'
@@ -20,13 +20,11 @@ us_calls = OrderedDict([('**Group A** (Extra Only)', ('**Any:** K, N, W (1x2)\n'
'**Alaska:** AL, KL, NL, WL (2x1)\n' '**Alaska:** AL, KL, NL, WL (2x1)\n'
'**Caribbean:** KP, NP, WP (2x1)\n' '**Caribbean:** KP, NP, WP (2x1)\n'
'**Pacific:** AH, KH, NH, WH (2x1)')), '**Pacific:** AH, KH, NH, WH (2x1)')),
('**Group B** (Advanced and Extra Only)', ('**Any:** K, N, W (1x2)\n' ('**Group B** (Advanced and Extra Only)', ('**Any:** KA-KZ, NA-NZ, WA-WZ (2x2)\n'
' AA-AL, KA-KZ, NA-NZ, WA-WZ (2x1)\n'
' AA-AL (2x2)\n'
'*Except*\n' '*Except*\n'
'**Alaska:** AL, KL, NL, WL (2x1)\n' '**Alaska:** AL (2x2)\n'
'**Caribbean:** KP, NP, WP (2x1)\n' '**Caribbean:** KP (2x2)\n'
'**Pacific:** AH, KH, NH, WH (2x1)')), '**Pacific:** AH (2x2)')),
('**Group C** (Technician, General, Advanced, Extra Only)', ('**Any Region:** K, N, W (1x3)\n' ('**Group C** (Technician, General, Advanced, Extra Only)', ('**Any Region:** K, N, W (1x3)\n'
'*Except*\n' '*Except*\n'
'**Alaska:** KL, NL, WL (2x2)\n' '**Alaska:** KL, NL, WL (2x2)\n'
@@ -48,10 +46,10 @@ us_calls = OrderedDict([('**Group A** (Extra Only)', ('**Any:** K, N, W (1x2)\n'
'now Republic of the Marshall Islands (V73)\n' 'now Republic of the Marshall Islands (V73)\n'
'- Any suffix SOS or QRA-QUZ\n' '- Any suffix SOS or QRA-QUZ\n'
'- Any 2x3 with X as the first suffix letter\n' '- Any 2x3 with X as the first suffix letter\n'
'- Any 2x3 with AF, KF, NF, or WF prefix and suffix EMA\n' '- Any 2x3 with AF, KF, NF, or WF prefix and suffix EMA: FEMA\n'
'- Any 2x3 with AA-AL, NA-NZ, WC, WK, WM, WR, or WT prefix\n' '- Any 2x3 with AA-AL, NA-NZ, WC, WK, WM, WR, or WT prefix: "Group X"\n'
'- Any 2x1, 2x2, or 2x3 with KP, NP, WP prefix and 0, 6, 7, 8, 9 number\n' '- Any 2x1, 2x2, or 2x3 with KP, NP, WP prefix and 0, 6, 7, 8, 9 number\n'
'- Any 1x1 callsign'))]) '- Any 1x1 callsign: Special Event'))])
# format: country: (title, description, text) # format: country: (title, description, text)
options = {'us': (us_calls_title, us_calls_desc, us_calls)} options = {'us': (us_calls_title, us_calls_desc, us_calls)}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 153 KiB

+7
View File
@@ -0,0 +1,7 @@
{
"ca": ["ca.png", "Canada", "Amateur radio bands in Canada", "**This bandplan is incomplete**; some bands, like 630m, are simply not present. It also does not cover any band above 30MHz.", "[RAC 0-30MHz Band Plan](https://www.rac.ca/wp-content/uploads/files/pdf/RAC%20Bandplan%20December%201%202015.pdf)", "🇨🇦"],
"cn": ["cn.png", "China", "Amateur radio bands in China", "", "Created by KN8U and NY7H", "🇨🇳"],
"mx": ["mx.png", "Mexico", "Radio allocations in Mexico", "Full radio allocations chart for all services. No information specific to amateur radio is shown.", "Secretaría de Comunicaciones y Transportes (SCT) / Instituto Federal de Telecomunicaciones (IFT)", "🇲🇽"],
"nl": ["nl.png", "Netherlands", "Amateur radio bands in the Netherlands", "", "", "🇳🇱"],
"us": ["us.png", "USA", "Amateur radio bands in the USA", "", "*[ARRL Frequency Chart](https://www.arrl.org/shop/ARRL-Frequency-Chart-11-17/)* [[PDF]](http://www.arrl.org/files/file/Regulatory/Band%20Chart/Band%20Chart%20-%2011X17%20Color.pdf)", "🇺🇸"]
}

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

Before

Width:  |  Height:  |  Size: 160 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Before

Width:  |  Height:  |  Size: 504 KiB

After

Width:  |  Height:  |  Size: 504 KiB

+6
View File
@@ -0,0 +1,6 @@
{
"arrl": ["arrl-rac.png", "ARRL Sections", "ARRL Sections", "", "", "🇺🇸"],
"rac": ["arrl-rac.png", "RAC Sections", "RAC Sections", "", "", "🇨🇦"],
"cn": ["cn.png", "China's Prefixes", "Map of prefix regions in China", "", "CRAC", "🇨🇳"],
"us": ["us.png", "USA's Prefixes", "Map of prefix regions in the USA", "", "*[ARRL WAS Map](https://www.arrl.org/was-forms)* [[PDF]](http://www.arrl.org/files/file/Awards%20Application%20Forms/WASmap_Color.pdf)", "🇺🇸"]
}

Before

Width:  |  Height:  |  Size: 136 KiB

After

Width:  |  Height:  |  Size: 136 KiB

-12
View File
@@ -1,12 +0,0 @@
{
"A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", "F": "..-.", "G": "--.",
"H": "....", "I": "..", "J": ".---", "K": "-.-", "L": ".-..", "M": "--", "N": "-.",
"O": "---", "P": ".--.", "Q": "--.-", "R": ".-.", "S": "...", "T": "-", "U": "..-",
"V": "...-", "W": ".--", "X": "-..-", "Y": "-.--", "Z": "--..", "1": ".----",
"2": "..---", "3": "...--", "4": "....-", "5": ".....", "6": "-....", "7": "--...",
"8": "---..", "9": "----.", "0": "-----", " ": " ", ".": ".-.-.-", ",": "--..--",
"?": "..--..", "'": ".----.", "!": "-.-.--", "/": "-..-.", "(": "-.--.", ")": "-.--.-",
"&": ".-...", ":": "---...", ";": "-.-.-.", "=": "-...-", "+": ".-.-.", "-": "-....-",
"\"": ".-..-.", "@": ".--.-.", "Ä": ".-.-", "Å": ".-.-", "Ą": ".-.-", "Æ": ".-.-",
"É": "..-..", "Ñ": "--.--", "Ö": "---.", "Ü": "..--", "Š": "----", " ": "/"
}
+29
View File
@@ -0,0 +1,29 @@
"""
A listing of morse code symbols
---
Copyright (C) 2019 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2.
"""
morse = {"A": ".-", "B": "-...", "C": "-.-.", "D": "-..", "E": ".", "F": "..-.", "G": "--.",
"H": "....", "I": "..", "J": ".---", "K": "-.-", "L": ".-..", "M": "--", "N": "-.",
"O": "---", "P": ".--.", "Q": "--.-", "R": ".-.", "S": "...", "T": "-", "U": "..-",
"V": "...-", "W": ".--", "X": "-..-", "Y": "-.--", "Z": "--..", "1": ".----",
"2": "..---", "3": "...--", "4": "....-", "5": ".....", "6": "-....", "7": "--...",
"8": "---..", "9": "----.", "0": "-----", ".": ".-.-.-", ",": "--..--", "?": "..--..",
"'": ".----.", "!": "-.-.--", "/": "-..-.", "(": "-.--.", ")": "-.--.-", "&": ".-...",
":": "---...", ";": "-.-.-.", "=": "-...-", "+": ".-.-.", "-": "-....-", "\"": ".-..-.",
"@": ".--.-.", "Ä": ".-.-", "Å": ".-.-", "Ą": ".-.-", "Æ": ".-.-", "É": "..-..",
"Ñ": "--.--", "Ö": "---.", "Ü": "..--", "Š": "----", " ": "/"}
ascii = {".-": "A", "-...": "B", "-.-.": "C", "-..": "D", ".": "E", "..-.": "F", "--.": "G",
"....": "H", "..": "I", ".---": "J", "-.-": "K", ".-..": "L", "--": "M", "-.": "N",
"---": "O", ".--.": "P", "--.-": "Q", ".-.": "R", "...": "S", "-": "T", "..-": "U",
"...-": "V", ".--": "W", "-..-": "X", "-.--": "Y", "--..": "Z", ".----": "1",
"..---": "2", "...--": "3", "....-": "4", ".....": "5", "-....": "6", "--...": "7",
"---..": "8", "----.": "9", "-----": "0", ".-.-.-": ".", "--..--": ",", "..--..": "?",
".----.": "'", "-.-.--": "!", "-..-.": "/", "-.--.": "(", "-.--.-": ")", ".-...": "&",
"---...": ":", "-.-.-.": ";", "-...-": "=", ".-.-.": "+", "-....-": "-", ".-..-.": "\"",
".--.-.": "@", ".-.-": "Ä", "..-..": "É", "--.--": "Ñ", "---.": "Ö", "..--": "Ü",
"----": "Š", "/": " "}
+14
View File
@@ -0,0 +1,14 @@
"""
A listing of NATO Phonetics
---
Copyright (C) 2019 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2.
"""
phonetics = {'a': 'alfa', 'b': 'bravo', 'c': 'charlie', 'd': 'delta', 'e': 'echo', 'f': 'foxtrot',
'g': 'golf', 'h': 'hotel', 'i': 'india', 'j': 'juliett', 'k': 'kilo', 'l': 'lima',
'm': 'mike', 'n': 'november', 'o': 'oscar', 'p': 'papa', 'q': 'quebec', 'r': 'romeo',
's': 'sierra', 't': 'tango', 'u': 'uniform', 'v': 'victor', 'w': 'whiskey', 'x': 'x-ray',
'y': 'yankee', 'z': 'zulu'}
-275
View File
@@ -1,275 +0,0 @@
{
"QAB": "May I have clearance (for ...) from ... (place) to ... (place) at flight level/altitude ... ? / You are cleared (or ... is cleared) by ... from ... (place) to ... (place) at flight level/altitude ...",
"QAF": "Will you advise me when you are (were) at (over) ... (place)? / I am (was) at (over) ... (place) (at ... hours) at flight level/altitude ...",
"QAG": "Arrange your flight in order to arrive over ... (place) at ... hours.",
"QAH": "What is your height above ... (datum)? / I am at .... flight level/altitude ... --or-- Arrange your flight so as to reach flight level/altitude ... at ... (hours or place).",
"QAI": "What is the essential traffic respecting my aircraft? / The essential traffic respecting your aircraft is ...",
"QAK": "Is there any risk of collision? / There is risk of collision.",
"QAL": "Are you going to land at ... (place)? I am going to land at ... (place).",
"QAM": "What is the latest available meteorological observation for ... (place)? / Meteorological observation made at ... (place) at ... hours was as follows ...",
"QAN": "What is the surface wind direction and speed at ... (place)? / The surface wind direction and speed at ... (place) at ... hours is ... (direction) ... (speed).",
"QAO": "What is the wind direction in degrees TRUE and speed at ... (position or zone/s) at each of the ... (figures) ... (units) levels above ... (datum)? / The wind direction and speed at (position or zone/s) at flight level/altitude ... is: ... (vertical distance) ... degrees TRUE ... (speed).",
"QAP": "Shall I listen for you (or for ...) on ... kHz (... MHz)? / Listen for me (or for ...) on ... kHz (... MHz).",
"QAQ": "Am I near a prohibited, restricted or danger area?",
"QAR": "May I stop listening on the watch frequency for ... minutes? / You may stop listening on the watch frequency for ... minutes.",
"QAU": "Where may I jettison fuel? / I am about to jettison fuel.",
"QAW": "I am about to carry out overshoot procedure.",
"QAY": "Will you advise me when you pass (passed) ... (place) bearing 090 (270) degrees relative to your heading? / I passed ... (place) bearing ... degrees relative to my heading at ... hours.",
"QAZ": "Are you experiencing communication difficulties through flying in a storm? / I am experiencing communication difficulties through flying in a storm.",
"QBA": "What is the horizontal visibility at ... (place)? / The horizontal visibility at ... (place) at ... hours is ... (distance figures and units).",
"QBB": "What is the amount, type and height above official aerodrome elevation of the base of the cloud [at ... (place)]? / The amount, type and height above official aerodrome elevation of the base of the cloud at ... (place) at ... hours is: ... eights (... type) at ... (figures and units) height above official aerodrome elevation.",
"QBC": "Report meteorological conditions as observed from your aircraft [at ... (position or zone)] [(at ... hours)]. / The meteorological conditions as observed from my aircraft at ... (position or zone) at ... hours at ... (figures and units) height above ... (datum) are ...",
"QBD": "How much fuel have you remaining (expressed as hours and/or minutes of consumption)? / My fuel endurance is ... (hours and/or minutes).",
"QBE": "I am about to wind in my aerial.",
"QBF": "Are you flying in cloud? / I am flying in cloud at ... flight level/altitude ... [and I am ascending (descending) to flight level/altitude ...].",
"QBG": "Are you flying above cloud? / I am flying above cloud and at flight level/altitude ...",
"QBH": "Are you flying below cloud? / I am flying below cloud and at flight level/altitude ...",
"QBI": "Is flight under IFR compulsory at ... (place) [or from ... to ... (place)]? / Flight under IFR is compulsory at ... (place) [or from ... to ... (place)].",
"QBJ": "What is the amount, type and height above ... (datum) of the top of the cloud [at ... (position or zone)]? / At ... hours at ... (position or zone) the top of the cloud is: amount ... eights (... type) at ... (figures and units) height above ... (datum).",
"QBK": "Are you flying with no cloud in your vicinity? / I am flying with no cloud in my vicinity and at flight level/altitude ...",
"QBM": "Has ... sent any messages for me? / Here is the message sent by ... at ... hours.",
"QBN": "Are you flying between two layers of cloud? / I am flying between two layers of cloud and at flight level/altitude ...",
"QBO": "What is the nearest aerodrome at which flight under VFR is permissible and which would be suitable for my landing? / Flying under VFR is permissible at ... (place) which would be suitable for your landing.",
"QBP": "Are you flying in and out of cloud? / I am flying in and out of cloud and at flight level/altitude ...",
"QBS": "Ascend (or descend) to ... (figures and units) height above ... (datum) before encountering instrument meteorological conditions or if visibility falls below ... (distance figures and units) and advise.",
"QBT": "What is the runway visual range at ... (place)? / The runway visual range at ... (place) at ... hours is ... (distance figures and units).",
"QBV": "Have you reached flight level/altitude ... [or ... (area or place)]? / I have reached ... flight level/altitude ... [or ... (area or place)].",
"QBX": "Have you left ... flight level/altitude ... [or ... (area or place)]? / I have left ... flight level/altitude ... [or ... (area or place)].",
"QBZ": "Report your flying conditions in relation to clouds. / The reply to QBZ ? is given by the appropriate answer form of signals QBF, QBG, QBH, QBK, QBN and QBP.",
"QCA": "May I change my flight level/altitude from ... to ... ? / You may change your flight level/altitude from ... to ...",
"QCB": "Delay is being caused by ...",
"QCE": "When may I expect approach clearance? / Expect approach clearance at ... hours.",
"QCF": "Delay indefinite. Expect approach clearance not later than ... hours.",
"QCH": "May I taxi to ... (place)? / Cleared to taxi to ... (place).",
"QCI": "Make a 360-degree turn immediately (turning to the ...).",
"QCS": "My reception on ... frequency has broken down.",
"QCX": "What is your full call sign? / My full call sign is ...",
"QCY": "I am working on a trailing aerial.",
"QDB": "Have you sent message ... to ... ? / I have sent message ... to ...",
"QDF": "What is your D-Value at ... (position)?",
"QDL": "Do you intend to ask me for a series of bearings? / I intend to ask you for a series of bearings.",
"QDM": "Will you indicate the MAGNETIC heading for me to steer towards you (or ...) with no wind? / The MAGNETIC heading for you to steer to reach me (or ...) with no wind was ... degrees (at ... hours).",
"QDP": "Will you accept control (or responsibility) of (for) ... now (or at ... hours)? / I will accept control (or responsibility) of (for) ... now (or at ... hours).",
"QDR": "What is my MAGNETIC bearing from you (or from ...)? / Your MAGNETIC bearing from me (or from ...) was ... degrees (at ... hours).",
"QDT": "Are you flying in visual meteorological condition? / I am flying in visual meteorological condition.",
"QDU": "Cancelling my IFR flight.",
"QDV": "Are you flying in a flight visibility of less than ... (figures and units)? / I am flying in a flight visibility of less than ... (figures and units) at flight level/altitude ...",
"QEA": "May I cross the runway ahead of me? / You may cross the runway ahead of you.",
"QEB": "May I turn at the intersection? / Taxi as follows at the intersection ...",
"QEC": "May I make a 180-degree turn and return down the runway? / You may make a 180-degree turn and return down the runway.",
"QED": "Shall I follow the pilot vehicle? / Follow the pilot vehicle.",
"QEF": "Have I reached my parking area? / You have reached your parking area.",
"QEG": "May I leave the parking area? / You may leave the parking area.",
"QEH": "May I move to the holding position for runway number ... ? / Cleared to the holding position for runway number ...",
"QEJ": "May I assume position for take-off? / Cleared to hold at take-off position for runway number ...",
"QEK": "Are you ready for immediate take-off? / I am ready for immediate take-off.",
"QEL": "May I take-off (and make a ... hand turn after take-off)? / You are cleared to take-off (turn as follows after take-off ...).",
"QEM": "What is the condition of the landing surface at ... (place)? / The condition of the landing surface at ... (place) is ...",
"QEN": "Shall I hold my position? / Hold your position",
"QEO": "Shall I clear the runway (or landing area)? / Clear the runway (or landing area).",
"QES": "Is a right-hand circuit in force at ... (place)? / A right-hand circuit is in force at ... (place).",
"QFA": "What is the meteorological forecast for ... (flight, route, section of route or zone) for the period ... hours until ... hours? / The meteorological forecast for ... (flight, route, section of route or zone) for the period ... hours until ... hours is ...",
"QFB": "The approach/runway lights are out of order.",
"QFC": "What is the amount, the type and the height above ... (datum) of the base of the cloud at ... (place, position or zone)? / At ... (place, position or zone) the base of the cloud is ... eighths ... type at ... (figures and units) height above ... (datum).",
"QFD": "Is the ... visual beacon [at ... (place)] in operation?",
"QFE": "What should I set on the subscale of my altimeter so that the instrument would indicate its height above the reference elevation being used? / If you set the subscale of your altimeter to read ... millibars, the instrument would indicate its height above aerodrome elevation (above threshold, runway number ...).",
"QFF": "[At ... (place)] what is the present atmospheric pressure converted to mean sea level in accordance with meteorological practice? / At ... (place) the atmospheric pressure converted to mean sea level in accordance with meteorological practice is (or was determined at ... hours to be) ... millibars.",
"QFG": "Am I overhead? / You are overhead.",
"QFH": "May I descend below the clouds? / You may descend below the clouds.",
"QFI": "Are the aerodrome lights lit? / The aerodrome lights are lit.",
"QFL": "Will you send up pyrotechnical lights? / I will send up pyrotechnical lights.",
"QFM": "What flight level/altitude should I maintain?",
"QFO": "May I land immediately? / You may land immediately.",
"QFP": "Will you give me the latest information concerning ... facility [at ... (place)]? / The latest information concerning ... facility [at ... (place)] is as follows ...",
"QFQ": "Are the approach and runway lights lit? / The approach and runway lights are lit.",
"QFR": "Does my landing gear appear damaged? / Your landing gear appears damaged.",
"QFS": "Is the radio facility at ... (place) in operation? / The radio facility at ... (place) is in operation (or will be in operation in ... hours).",
"QFT": "Between what heights above ... (datum) has ice formation been observed [at ... (position or zone)]? / Ice formation has been observed at ... (position or zone) in the type of ... and with an accretion rate of ... between ... (figures and units) and ... (figures and units) heights above ... (datum).",
"QFU": "What is the magnetic direction (or number) of the runway to be used? / The magnetic direction (or number) of the runway to be used is ...",
"QFV": "Are the floodlights switched on? / The floodlights are switched on.",
"QFW": "What is the length of the runway in use in ... (units)? / The length of runway ... now in use is ... (figures and units).",
"QFX": "I am working (or am going to work) on a fixed aerial.",
"QFY": "Please report the present meteorological landing conditions [at ... (place)]. / The present meteorological landing conditions at ... (place) are ...",
"QFZ": "What is the aerodrome meteorological forecast for ... (place) for the period ... hours until ... hours? / The aerodrome meteorological forecast for ... (place) for the period ... hours until ... hours is ...",
"QGC": "There are obstructions to the ... of ... runway ...",
"QGD": "Are there on my track any obstructions whose elevation equals or exceeds my altitude? / There are obstructions on your track ... (figures and units) height above ... (datum).",
"QGE": "What is my distance to your station (or to ...)? / Your distance to my station (or to ...) is ... (distance figures and units).",
"QGH": "May I land using ... (procedure or facility)? / You may land using ... (procedure or facility).",
"QGK": "What track should I make good? / Make good a track from ... (place) on ... degrees ... (true or magnetic).",
"QGL": "May I enter the ... (control area or zone) at ... (place)? / You may enter the ... (control area or zone) at ... (place).",
"QGM": "Leave the ... (control area or zone).",
"QGN": "May I be cleared to land [at ... (place)]? / You are cleared to land [at ... (place)].",
"QGO": "Landing is prohibited at ... (place).",
"QGP": "What is my number for landing? / You are number ... to land.",
"QGQ": "May I hold at ... (place)? / Hold at ... (place) at flight level/altitude ... (datum) and await further clearance.",
"QGT": "Fly for ... minutes on a heading what will enable you to maintain a track reciprocal to your present one.",
"QGU": "Fly for ... minutes on a magnetic heading of ... degrees.",
"QGV": "Do you see me? / Can you see the aerodrome? / Can you see ... (aircraft)? / I see you at ... (cardinal or quadrantal point of direction).",
"QGW": "Does my landing gear appear to be down and in place? / Your landing gear appears to be down and in place.",
"QGZ": "Hold on ... direction of ... facility.",
"QHE": "Will you inform me when you are on ... leg of approach? / I am on ...(leg).. of approach.",
"QHG": "May I enter traffic circuit at flight level/altitude ...? / Cleared to enter traffic circuit at flight level/altitude ...",
"QHH": "Are you making an emergency landing? / I am making an emergency landing.",
"QHI": "Are you (or is ...) ... waterborne / on land? ",
"QHQ": "May I make a ... approach [at ... (place)]? / You may make a ... approach [at ... (place)].",
"QHZ": "Shall I circle the aerodrome (or go around)? / Circle the aerodrome (or go around).",
"QIC": "May I establish communication with ... radio station on ... kHz (or ... MHz.) now (or at ... hours)? / Establish communication with ... radio station on ... kHz. (or MHz.) now (or at ...hours).",
"QIF": "What frequency is ... using? / ... is using ... kHz (or ... MHz.).",
"QJA": "Is my tape / mark and space reversed? / Your tape / mark and space is reversed.",
"QJB": "Will you use radio/cable/telegraph/teletype/telephone/receiver/transmitter/reperforator? / I will use ...",
"QJC": "Will you check your transmitter/autohead/perforator/reperfordator/printer/keyboard/antenna? / I will check my ...",
"QJD": "Am I transmitting letters/figures? / You are transmitting ...",
"QJE": "Is my frequency shift wide/narrow/correct? / Your frequency shift is ... (by ... cycles)",
"QJF": "My signal as checked by monitor locally/as radiated is satisfactory.",
"QJG": "Shall I revert to automatic relay? / Revert to automatic relay.",
"QJH": "Shall I run test tape/sentence? / Run test tape/sentence.",
"QJI": "Will you transmit a continuous mark/space? / I am transmitting a continuous mark/space.",
"QJK": "Are you receiving continuous mark/space / mark/space bias? / I am receiving a continuous mark/space / mark/space bias.",
"QKC": "The sea conditions (at ... position) ... permit alighting but not takeoff / render alihting extremely hazardous.",
"QKF": "May I be relieved (at ... hours)? / You may expect to be relieved at ... hours [by aircraft/vessel/callsign/name].",
"QKG": "Will relief take place when ... (identification) establishes visual/comms contact with survivors? / Relief will take place when ... (identification) establishes visual/comms contact with survivors.",
"QKH": "Report details of the parallel sweep (track) search being (or to be) conducted? --or-- In the parallel sweep (track) search being (or to be) conducted, what is (are) the direction/separation/altitude of sweeps employed in the search pattern? / The parallel sweep (track) search is being (or to be) conducted [with direction of sweeps ... degrees ... (true or magnetic) || with ... (distance figures and units) separation between sweeps || flight level/altitude].",
"QKN": "Aircraft plotted (believed to be you) in position ... on track ... degrees at ... hours.",
"QKO": "What other units are (or will be) taking part in the operation [... (identification of operation)]? / In the operation [... (identification)], the following units are (or will be) taking part ... (name of units). --or-- ... (name) unit is taking part in operation [... (identification] (with effect from ... hours).",
"QKP": "Which pattern of search is being followed? / The search pattern is parallel sweep/square search/creeping line ahead/track crawl/contour search/combined search by aircraft and ship/[other].",
"QLB": "Will you monitor ... station and report regarding range, quality, etc.? / I have monitored ... station and report (briefly) as follows ...",
"QLE": "What is your expected signal? / The expected signal is low...",
"QLF": "Are you sending with your left foot? Try sending with your left foot!",
"QLH": "Will you use simultaneous keying on ... frequency and ... frequency? / I will now key simultaneously on ... frequency and ... frequency.",
"QLV": "Is the ... radio facility still required? / The ... radio facility is still required.",
"QMH": "Shift to transmit and receive on ... kHz (or ... MHz.); if communication is not established within 5 minutes, revert to present frequency.",
"QMI": "Report the vertical distribution of cloud [at ... (position or zone)] as observed from your aircraft. / The vertical distribution of cloud as observed from my aircraft at ... hours at ... (position or zone) is : *lowest layer observed* ... eights (... type) with base of ... (figures and units) and tops of ... (figures and units) [*and similarly in sequence for each of the layers observed.] height above ... (datum).",
"QMU": "What is the surface temperature at ... (place) and what is the dew point temperature at that place? / The surface temperature at ... (place) at ... hours is ... degrees and the dew point temperature at that time and place is ... degrees.",
"QMW": "At ... (position or zone) what is (are) the flight level(s)/altitude(s) ... of the zero Celsius isotherm(s)? / At ... (position or zone) the zero Celsius isotherm(s) is (are) at flight level(s)/altitude(s) ...",
"QMX": "What is the air temperature [at ... (position or zone)] (at ... hours) at flight level/altitude ...? / At ... (position or zone) at ... hours the air temperature is ... (degrees and units) at flight level/altitude ... --Note-- Aircraft reporting QMX information will transmit the temperature figures as corrected for airspeed.",
"QMZ": "Have you any amendments to the flight forecast in respect of section of route yet to be traversed? / The following amendment(s) should be made to the flight forecast ... [If no amendments, signal QMZ NIL]",
"QNE": "What indication will my altimeter give on landing at ... (place) at ... hours, my sub-scale being set to 1013.2 millibars (29.92 inches)? / On landing at ... (place) at ... hours, with your sub-scale being set to 1013.2 millibars (29.92 inches), your altimeter will indicate ... (figures and units).",
"QNH": "What should I set on the subscale of my altimeter so that the instrument would indicate its elevation if my aircraft were on the ground at your station? / If you set the subscale of your altimeter to read ... millibars, the instrument would indicate its elevation if your aircraft were on the ground at my station at ... hours. --Note-- When the setting is given in hundredths of inch the abbreviation INS is used to identify the units.",
"QNI": "May I join the net? / You may check in...",
"QNO": "I am not equipped to give the information (or provide the facility) requested.",
"QNR": "I am approaching my point of no return.",
"QNT": "What is the maximum speed of the surface wind at ... (place)? / The maximum speed of the surface wind at ... (place) at ... hours is ... (speed figures and units).",
"QNY": "What is the present weather and the intensity thereof at ... (place, position or zone)? / The present weather and intensity thereof at ... (place, position or zone) at ... hours is ...",
"QOA": "Can you communicate by radiotelegraphy (500 kHz)? / I can communicate by radiotelegraphy (500 kHz).",
"QOB": "Can you communicate by radiotelephony (2182 kHz)? / I can communicate by radiotelephony (2182 kHz).",
"QOC": "Can you communicate by radiotelephony (channel 16 - frequency 156.80 MHz)? / I can communicate by radiotelephony (channel 16 - frequency 156.80 MHz).",
"QOD": "Can you communicate with me in Dutch/English/French/German/Greek/Italian/Japanese/Norwegian/Russian/Spanish? / I can communicate with you in ...",
"QOE": "Have you received the safety signal sent by ... (name and/or call sign)? / I have received the safety signal sent by ... (name and/or call sign).",
"QOF": "What is the commercial quality of my signals? / The quality of your signals is not commercial/marginally commercial/commercial.",
"QOG": "How many tapes have you to send? / I have ... tapes to send.",
"QOH": "Shall I send a phasing signal for ... seconds? / Send a phasing signal for ... seconds.",
"QOI": "Shall I send my tape? / Send your tape.",
"QOJ": "Will you listen on ... kHz (or MHz) for signals of emergency position-indicating radiobeacons? / I am listening on ... kHz (or MHz) for signals of emergency position-indicating radiobeacons.",
"QOK": "Have you received the signals of an emergency position-indicating radiobeacon on ... kHz (or MHz)? / I have received the signals of an emergency position-indicating radiobeacon on ... kHz (or MHz).",
"QOL": "Is your vessel fitted for reception of selective calls? If so, what is your selective call number or signal? / My vessel is fitted for the reception of selective calls. My selective call number or signal is ...",
"QOM": "On what frequencies can your vessel be reached by a selective call? / My vessel can be reached by a selective call on the following frequency/ies ... (periods of time to be added if necessary).",
"QOO": "Can you send on any working frequency? / I can send on any working frequency.",
"QOT": "Do you hear my call; what is the approximate delay in minutes before we may exchange traffic? / I hear your call; the approximate delay is ... minutes.",
"QRA": "What is the name of your vessel (or station)? / The name of my vessel (or station) is ...",
"QRB": "How far approximately are you from my station? / The approximate distance between our stations is ... nautical miles (or km).",
"QRC": "What is your true bearing? / My true bearing is ____ degrees.",
"QRD": "Where are you bound for? / I am bound for ____.",
"QRE": "What is your estimated time of arrival at ... (or over ...) (place)? / My estimated time of arrival at ... (or over ...) (place) is ... hours.",
"QRF": "Where are you bound from? / I am bound from ____.",
"QRG": "Will you tell me my exact frequency (or that of ...)? / Your exact frequency (or that of ...) is ... kHz (or MHz).",
"QRH": "Does my frequency vary? / Your frequency varies.",
"QRI": "How is the tone of my transmission? / The tone of your transmission is ...",
"QRJ": "How many voice contacts do you want to make? / I want to make ... voice contacts.",
"QRK": "How do you receive me? / I am receiving (1-5).",
"QRL": "Are you busy? / I am busy (or I am busy with ...). Please do not interfere.",
"QRM": "Are you being interfered with? / I am being interfered with.",
"QRN": "Are the atmospherics strong? / Atmospherics (noise) are very strong.",
"QRO": "Shall I increase transmitter power? / Increase transmitter power.",
"QRP": "Shall I decrease transmitter power? / Decrease transmitter power.",
"QRQ": "Shall I send faster? / Send faster (... wpm)",
"QRR": "Are you ready for automatic operation? / I am ready for automatic operation. Send at ... words per minute.",
"QRS": "Shall I send more slowly? / Send more slowly (... words per minute).",
"QRT": "Shall I stop sending? / Stop sending.",
"QRU": "Have you anything for me? / I have nothing for you.",
"QRV": "Are you ready? / I am ready.",
"QRW": "Shall I inform ... that you are calling him on ... kHz (or MHz)? / Please inform ... that I am calling him on ... kHz (or MHz).",
"QRX": "When will you call me again? / I will call you again at ... hours (on ... kHz (or MHz)).",
"QRY": "What is my turn? / Your turn is Number ... (or according to any other indication).",
"QRZ": "Who is calling me? / You are being called by ... (on ... kHz (or MHz)).",
"QSA": "What is the strength of my signals (or those of ...)? / The strength of your signals (or those of ...) is ...",
"QSB": "Are my signals fading? / Your signals are fading.",
"QSC": "Are you a cargo vessel? --or-- Are you a low traffic ship? / I am a cargo vessel. --or-- I am a low traffic ship.",
"QSD": "Is my keying defective? --or-- Are my signals mutilated? / Your keying is defective. --or-- Your signals are mutilated.",
"QSE": "What is the estimated drift of the survival craft? / The estimated drift of the survival craft is ... (figures and units).",
"QSF": "Have you effected rescue? / I have effected rescue and am proceeding to ... base (with ... persons injured requiring ambulance).",
"QSG": "Shall I send ... telegrams at a time? / Send ... telegrams at a time.",
"QSH": "Are you able to home on your direction-finding equipment? / I am able to home on my D/F equipment (on station ...).",
"QSI": "I have been unable to break in on your transmission. --or-- Will you inform ... (call sign) that I have been unable to break in on his transmission (on ... kHz (or MHz)).",
"QSJ": "What is the charge to be collected to ... including your internal charge? / The charge to be collected to ... including my internal charge is ... francs.",
"QSK": "Can you hear me between your signals and if so can I break in on your transmission? / I can hear you between my signals; break in on my transmission.",
"QSL": "Can you acknowledge receipt? / I am acknowledging receipt.",
"QSM": "Shall I repeat the last telegram (message) which I sent you, or some previous telegram (message)? / Repeat the last telegram (message) which you sent me (or telegram(s) (message(s)) numbers(s) ...).",
"QSN": "Did you hear me (or ... (call sign)) on ... kHz (or MHz)? / I did hear you (or ... (call sign)) on ... kHz (or MHz).",
"QSO": "Can you communicate with ... direct (or by relay)? / I can communicate with ... direct (or by relay through ...).",
"QSP": "Will you relay to ... free of charge? / I will relay to ... free of charge.",
"QSQ": "Have you a doctor on board (or is ... (name of person) on board)? / I have a doctor on board (or ... (name of person) is on board).",
"QSR": "Shall I repeat the call on the calling frequency? / Repeat your call on the calling frequency; did not hear you (or have interference).",
"QSS": "What working frequency will you use? / I will use the working frequency ... kHz (or MHz) (in the HF bands normally only the last three figures of the frequency need be given).",
"QST": "Here is a broadcast message to all amateurs.",
"QSU": "Shall I send or reply on this frequency (or on ... kHz (or MHz)) (with emissions of class ...)? / Send or reply on this frequency (or on ... kHz (or MHz)) (with emissions of class ...).",
"QSV": "Shall I send a series of Vs on this frequency (or on ... kHz (or MHz))? / Send a series of Vs on this frequency (or on ... kHz (or MHz)).",
"QSW": "Will you send on this frequency (or on ... kHz (or MHz)) (with emissions of class ...)? / I am going to send on this frequency (or on ... kHz (or MHz)) (with emissions of class ...).",
"QSX": "Will you listen to ... (call sign(s)) on ... kHz (or MHz)? --or-- Will you listen to ... (call sign(s)) on ... kHz (or MHz), or in the bands ... / channels ... ? / I am listening to ... (call sign(s)) on ... kHz (or MHz). --or-- I am listening to ... (call sign(s)) on ... kHz (or MHz), or in the bands ... / channels ...",
"QSY": "Change to transmission on another frequency",
"QSY": "Shall I change to transmission on another frequency? / Change to transmission on another frequency (or on ... kHz (or MHz)).",
"QSZ": "Shall I send each word or group more than once? / Send each word or group twice (or ... times).",
"QTA": "Shall I cancel telegram (or message) number ... ? / Cancel telegram (or message) number ...",
"QTB": "Do you agree with my counting of words? / I do not agree with your counting of words; I will repeat the first letter or digit of each word or group.",
"QTC": "How many telegrams have you to send? / I have ... telegrams for you (or for ...).",
"QTD": "What has the rescue vessel or rescue aircraft recovered? / ... (identification) has recovered ... survivors/wreckage/bodies.",
"QTE": "What is my TRUE bearing from you? --or-- What is my TRUE bearing from ... (call sign)? --or-- What is the TRUE bearing of ... (call sign) from ... (call sign)? / Your TRUE bearing from me is ... degrees at ... hours. --or-- Your TRUE bearing from ... (call sign) was ... degrees at ... hours. --or-- The TRUE bearing of ... (call sign) from ... (call sign) was ... degrees at ... hours.",
"QTF": "Will you give me the position of my station according to the bearings taken by the direction-finding stations which you control? / The position of your station according to the bearings taken by the D/F stations which I control was ... latitude, ... longitude (or other indication of position), class ... at ... hours.",
"QTG": "Will you send two dashes of ten seconds each followed by your call sign (repeated ... times) (on ... kHz (or MHz))? --or-- Will you request ... to send two dashes of ten seconds followed by his call sign (repeated ... times) on ... kHz (or MHz)? / I am going to send two dashes of ten seconds each followed by my call sign (repeated ... times) (on ... kHz (or MHz)). --or-- I have requested ... to send two dashes of ten seconds followed by his call sign (repeated ... times) on ... kHz (or MHz).",
"QTH": "My location is",
"QTHR": "At the registered location; Chiefly British in voice or writing, Historically - the location in the printed Callbook. Modernly - as given in online government records for my callsign",
"QTH": "What is your position in latitude and longitude (or according to any other indication)? / My position is ... latitude, ... longitude (or according to any other indication).",
"QTI": "What is your TRUE course? / My TRUE course is ... degrees.",
"QTJ": "What is your speed? / My speed is ... knots (or ... kilometres per hour or ... statute miles per hour).",
"QTK": "What is the speed of your aircraft in relation to the surface of the Earth? / The speed of my aircraft in relation to the surface of the Earth is ... knots (or ... kilometres per hour or ... statute miles per hour).",
"QTL": "What is your TRUE heading? / My TRUE heading is ... degrees.",
"QTM": "What is your MAGNETIC heading? / My MAGNETIC heading is ... degrees.",
"QTN": "At what time did you depart from ... (place)? / I departed from ... (place) at ... hours.",
"QTO": "Have you left dock (or port)? --or-- Are you airborne? / I have left dock (or port). --or-- I am airborne.",
"QTP": "Are you going to enter dock (or port)? --or-- Are you going to alight (or land)? / I am going to enter dock (or port). --or-- I am going to alight (or land).",
"QTQ": "Can you communicate with my station by means of the International Code of Signals (INTERCO)? / I am going to communicate with your station by means of the International Code of Signals (INTERCO).",
"QTR": "What is the correct time? / The correct time is ... hours.",
"QTS": "Will you send your call sign (and/or name) for ... seconds? / I will send my call sign (and/or name) for ... seconds.",
"QTT": "The identification signal which follows is superimposed on another transmission.",
"QTU": "What are the hours during which your station is open? / My station is open from ... to ... hours.",
"QTV": "Shall I stand guard for you on the frequency of ... kHz (or MHz) (from ... to ... hours)? / Stand guard for me on the frequency of ... kHz (or MHz) (from ... to ... hours).",
"QTW": "What is the condition of survivors? / Survivors are in ... condition and urgently need ...",
"QTX": "Will you keep your station open for further communication with me until further notice (or until ... hours)? / I will keep my station open for further communication with you until further notice (or until ... hours).",
"QTY": "Are you proceeding to the position of incident and if so when do you expect to arrive? / I am proceeding to the position of incident and expect to arrive at ... hours (on ... (date)).",
"QTZ": "Are you continuing the search? / I am continuing the search for ... (aircraft, ship, survival craft, survivors or wreckage).",
"QUA": "Have you news of ... (call sign)? / Here is news of ... (call sign).",
"QUB": "Can you give me in the following order information concerning: the direction in degrees TRUE and speed of the surface wind; visibility; present weather; and amount, type and height of base of cloud above surface elevation at ... (place of observation)? / Here is the information requested: ... (The units used for speed and distances should be indicated.)",
"QUC": "What is the number (or other indication) of the last message you received from me (or from ... (call sign))? / The number (or other indication) of the last message I received from you (or from ... (call sign)) is ...",
"QUD": "Have you received the urgency signal sent by ... (call sign of mobile station)? / I have received the urgency signal sent by ... (call sign of mobile station) at ... hours.",
"QUE": "Can you speak in ... (language), - with interpreter if necessary; if so, on what frequencies? / I can speak in ... (language) on ... kHz (or MHz).",
"QUF": "Have you received the distress signal sent by ... (call sign of mobile station)? / I have received the distress signal sent by ... (call sign of mobile station) at ... hours.",
"QUG": "Will you be forced to alight (or land)? / I am forced to alight (or land) immediately. --or-- I shall be forced to alight (or land) at ... (position or place) at ... hours.",
"QUH": "Will you give me the present barometric pressure at sea level? / The present barometric pressure at sea level is ... (units).",
"QUI": "Are your navigation lights working? / My navigation lights are working.",
"QUJ": "Will you indicate the TRUE track to reach you (or ...)? / The TRUE track to reach me (or ...) is ... degrees at ... hours.",
"QUK": "Can you tell me the condition of the sea observed at ... (place or coordinates)? / The sea at ... (place or coordinates) is ...",
"QUL": "Can you tell me the swell observed at ... (place or coordinates)? / The swell at ... (place or coordinates) is ...",
"QUM": "May I resume normal working? / Normal working may be resumed.",
"QUN": "When directed to all stations: Will vessels in my immediate vicinity ... (or in the vicinity of ... latitude, ... longitude) please indicate their position, TRUE course and speed? --or-- When directed to a single station: please indicate their position, TRUE course and speed? / My position, TRUE course and speed are ...",
"QUO": "Shall I search for aircraft/ship/survival craft in the vicinity of ... latitude, ... longitude (or according to any other indication)? / Please search for aircraft/ship/survival craft in the vicinity of ... latitude, ... longitude (or according to any other indication).",
"QUP": "Will you indicate your position by searchlight/black smoke trail/pyrotechnic lights? / My position is indicated by searchlight/black smoke trail/pyrotechnic lights.",
"QUQ": "Shall I train my searchlight nearly vertical on a cloud, occulting if possible and, if your aircraft is seen, deflect the beam up wind and on the water (or land) to facilitate your landing? / Please train your searchlight on a cloud, occulting if possible and, if my aircraft is seen or heard, deflect the beam up wind and on the water (or land) to facilitate my landing.",
"QUR": "Have survivors received survival equipment/been picked up by rescue vessel/been reached by ground rescue party? / Survivors are in possession of survival equipment dropped by ... / have been picked up by rescue vessel/have been reached by ground rescue party.",
"QUS": "Have you sighted survivors or wreckage? / If so, in what position? / Have sighted ... survivors in water/survivors on rafts/wreckage in position ... latitude, ... longitude (or according to any other indication).",
"QUT": "Is position of incident marked? / Position of incident is marked by flame or smoke float/sea marker/sea marker dye/... (specify other marking).",
"QUU": "Shall I home ship or aircraft to my position? / Home ship or aircraft ... (call sign) ... to your position by transmitting your call sign and long dashes on ... kHz (or MHz)/by transmitting on ... kHz (or MHz) TRUE track to reach you.",
"QUW": "Are you in the search area designated as ... (designator or latitude and longitude)? / I am in the ... (designation) search area.",
"QUX": "Do you have any navigational warnings or gale warnings in force? / I have the following navigational warning(s) or gale warning(s) in force: ...",
"QUY": "Is position of survival craft marked? / Position of survival craft was marked at ... hours by flame or smoke float/sea marker/sea marker dye/... (specify other marking).",
"QUZ": "May I resume restricted working? / Distress phase still in force; restricted working may be resumed.",
"QZZ": "Daily key change about to take place (German WWII usage)."
}
+280
View File
@@ -0,0 +1,280 @@
"""
A listing of Q Codes
---
Copyright (C) 2019 Abigail Gold, 0x5c
This file is part of discord-qrmbot and is released under the terms of the GNU
General Public License, version 2.
"""
# flake8: noqa
qcodes = {"QAB": "May I have clearance (for ...) from ... (place) to ... (place) at flight level/altitude ... ? / You are cleared (or ... is cleared) by ... from ... (place) to ... (place) at flight level/altitude ...",
"QAF": "Will you advise me when you are (were) at (over) ... (place)? / I am (was) at (over) ... (place) (at ... hours) at flight level/altitude ...",
"QAG": "Arrange your flight in order to arrive over ... (place) at ... hours.",
"QAH": "What is your height above ... (datum)? / I am at .... flight level/altitude ... --or-- Arrange your flight so as to reach flight level/altitude ... at ... (hours or place).",
"QAI": "What is the essential traffic respecting my aircraft? / The essential traffic respecting your aircraft is ...",
"QAK": "Is there any risk of collision? / There is risk of collision.",
"QAL": "Are you going to land at ... (place)? I am going to land at ... (place).",
"QAM": "What is the latest available meteorological observation for ... (place)? / Meteorological observation made at ... (place) at ... hours was as follows ...",
"QAN": "What is the surface wind direction and speed at ... (place)? / The surface wind direction and speed at ... (place) at ... hours is ... (direction) ... (speed).",
"QAO": "What is the wind direction in degrees TRUE and speed at ... (position or zone/s) at each of the ... (figures) ... (units) levels above ... (datum)? / The wind direction and speed at (position or zone/s) at flight level/altitude ... is: ... (vertical distance) ... degrees TRUE ... (speed).",
"QAP": "Shall I listen for you (or for ...) on ... kHz (... MHz)? / Listen for me (or for ...) on ... kHz (... MHz).",
"QAQ": "Am I near a prohibited, restricted or danger area?",
"QAR": "May I stop listening on the watch frequency for ... minutes? / You may stop listening on the watch frequency for ... minutes.",
"QAU": "Where may I jettison fuel? / I am about to jettison fuel.",
"QAW": "I am about to carry out overshoot procedure.",
"QAY": "Will you advise me when you pass (passed) ... (place) bearing 090 (270) degrees relative to your heading? / I passed ... (place) bearing ... degrees relative to my heading at ... hours.",
"QAZ": "Are you experiencing communication difficulties through flying in a storm? / I am experiencing communication difficulties through flying in a storm.",
"QBA": "What is the horizontal visibility at ... (place)? / The horizontal visibility at ... (place) at ... hours is ... (distance figures and units).",
"QBB": "What is the amount, type and height above official aerodrome elevation of the base of the cloud [at ... (place)]? / The amount, type and height above official aerodrome elevation of the base of the cloud at ... (place) at ... hours is: ... eights (... type) at ... (figures and units) height above official aerodrome elevation.",
"QBC": "Report meteorological conditions as observed from your aircraft [at ... (position or zone)] [(at ... hours)]. / The meteorological conditions as observed from my aircraft at ... (position or zone) at ... hours at ... (figures and units) height above ... (datum) are ...",
"QBD": "How much fuel have you remaining (expressed as hours and/or minutes of consumption)? / My fuel endurance is ... (hours and/or minutes).",
"QBE": "I am about to wind in my aerial.",
"QBF": "Are you flying in cloud? / I am flying in cloud at ... flight level/altitude ... [and I am ascending (descending) to flight level/altitude ...].",
"QBG": "Are you flying above cloud? / I am flying above cloud and at flight level/altitude ...",
"QBH": "Are you flying below cloud? / I am flying below cloud and at flight level/altitude ...",
"QBI": "Is flight under IFR compulsory at ... (place) [or from ... to ... (place)]? / Flight under IFR is compulsory at ... (place) [or from ... to ... (place)].",
"QBJ": "What is the amount, type and height above ... (datum) of the top of the cloud [at ... (position or zone)]? / At ... hours at ... (position or zone) the top of the cloud is: amount ... eights (... type) at ... (figures and units) height above ... (datum).",
"QBK": "Are you flying with no cloud in your vicinity? / I am flying with no cloud in my vicinity and at flight level/altitude ...",
"QBM": "Has ... sent any messages for me? / Here is the message sent by ... at ... hours.",
"QBN": "Are you flying between two layers of cloud? / I am flying between two layers of cloud and at flight level/altitude ...",
"QBO": "What is the nearest aerodrome at which flight under VFR is permissible and which would be suitable for my landing? / Flying under VFR is permissible at ... (place) which would be suitable for your landing.",
"QBP": "Are you flying in and out of cloud? / I am flying in and out of cloud and at flight level/altitude ...",
"QBS": "Ascend (or descend) to ... (figures and units) height above ... (datum) before encountering instrument meteorological conditions or if visibility falls below ... (distance figures and units) and advise.",
"QBT": "What is the runway visual range at ... (place)? / The runway visual range at ... (place) at ... hours is ... (distance figures and units).",
"QBV": "Have you reached flight level/altitude ... [or ... (area or place)]? / I have reached ... flight level/altitude ... [or ... (area or place)].",
"QBX": "Have you left ... flight level/altitude ... [or ... (area or place)]? / I have left ... flight level/altitude ... [or ... (area or place)].",
"QBZ": "Report your flying conditions in relation to clouds. / The reply to QBZ ? is given by the appropriate answer form of signals QBF, QBG, QBH, QBK, QBN and QBP.",
"QCA": "May I change my flight level/altitude from ... to ... ? / You may change your flight level/altitude from ... to ...",
"QCB": "Delay is being caused by ...",
"QCE": "When may I expect approach clearance? / Expect approach clearance at ... hours.",
"QCF": "Delay indefinite. Expect approach clearance not later than ... hours.",
"QCH": "May I taxi to ... (place)? / Cleared to taxi to ... (place).",
"QCI": "Make a 360-degree turn immediately (turning to the ...).",
"QCS": "My reception on ... frequency has broken down.",
"QCX": "What is your full call sign? / My full call sign is ...",
"QCY": "I am working on a trailing aerial.",
"QDB": "Have you sent message ... to ... ? / I have sent message ... to ...",
"QDF": "What is your D-Value at ... (position)?",
"QDL": "Do you intend to ask me for a series of bearings? / I intend to ask you for a series of bearings.",
"QDM": "Will you indicate the MAGNETIC heading for me to steer towards you (or ...) with no wind? / The MAGNETIC heading for you to steer to reach me (or ...) with no wind was ... degrees (at ... hours).",
"QDP": "Will you accept control (or responsibility) of (for) ... now (or at ... hours)? / I will accept control (or responsibility) of (for) ... now (or at ... hours).",
"QDR": "What is my MAGNETIC bearing from you (or from ...)? / Your MAGNETIC bearing from me (or from ...) was ... degrees (at ... hours).",
"QDT": "Are you flying in visual meteorological condition? / I am flying in visual meteorological condition.",
"QDU": "Cancelling my IFR flight.",
"QDV": "Are you flying in a flight visibility of less than ... (figures and units)? / I am flying in a flight visibility of less than ... (figures and units) at flight level/altitude ...",
"QEA": "May I cross the runway ahead of me? / You may cross the runway ahead of you.",
"QEB": "May I turn at the intersection? / Taxi as follows at the intersection ...",
"QEC": "May I make a 180-degree turn and return down the runway? / You may make a 180-degree turn and return down the runway.",
"QED": "Shall I follow the pilot vehicle? / Follow the pilot vehicle.",
"QEF": "Have I reached my parking area? / You have reached your parking area.",
"QEG": "May I leave the parking area? / You may leave the parking area.",
"QEH": "May I move to the holding position for runway number ... ? / Cleared to the holding position for runway number ...",
"QEJ": "May I assume position for take-off? / Cleared to hold at take-off position for runway number ...",
"QEK": "Are you ready for immediate take-off? / I am ready for immediate take-off.",
"QEL": "May I take-off (and make a ... hand turn after take-off)? / You are cleared to take-off (turn as follows after take-off ...).",
"QEM": "What is the condition of the landing surface at ... (place)? / The condition of the landing surface at ... (place) is ...",
"QEN": "Shall I hold my position? / Hold your position",
"QEO": "Shall I clear the runway (or landing area)? / Clear the runway (or landing area).",
"QES": "Is a right-hand circuit in force at ... (place)? / A right-hand circuit is in force at ... (place).",
"QFA": "What is the meteorological forecast for ... (flight, route, section of route or zone) for the period ... hours until ... hours? / The meteorological forecast for ... (flight, route, section of route or zone) for the period ... hours until ... hours is ...",
"QFB": "The approach/runway lights are out of order.",
"QFC": "What is the amount, the type and the height above ... (datum) of the base of the cloud at ... (place, position or zone)? / At ... (place, position or zone) the base of the cloud is ... eighths ... type at ... (figures and units) height above ... (datum).",
"QFD": "Is the ... visual beacon [at ... (place)] in operation?",
"QFE": "What should I set on the subscale of my altimeter so that the instrument would indicate its height above the reference elevation being used? / If you set the subscale of your altimeter to read ... millibars, the instrument would indicate its height above aerodrome elevation (above threshold, runway number ...).",
"QFF": "[At ... (place)] what is the present atmospheric pressure converted to mean sea level in accordance with meteorological practice? / At ... (place) the atmospheric pressure converted to mean sea level in accordance with meteorological practice is (or was determined at ... hours to be) ... millibars.",
"QFG": "Am I overhead? / You are overhead.",
"QFH": "May I descend below the clouds? / You may descend below the clouds.",
"QFI": "Are the aerodrome lights lit? / The aerodrome lights are lit.",
"QFL": "Will you send up pyrotechnical lights? / I will send up pyrotechnical lights.",
"QFM": "What flight level/altitude should I maintain?",
"QFO": "May I land immediately? / You may land immediately.",
"QFP": "Will you give me the latest information concerning ... facility [at ... (place)]? / The latest information concerning ... facility [at ... (place)] is as follows ...",
"QFQ": "Are the approach and runway lights lit? / The approach and runway lights are lit.",
"QFR": "Does my landing gear appear damaged? / Your landing gear appears damaged.",
"QFS": "Is the radio facility at ... (place) in operation? / The radio facility at ... (place) is in operation (or will be in operation in ... hours).",
"QFT": "Between what heights above ... (datum) has ice formation been observed [at ... (position or zone)]? / Ice formation has been observed at ... (position or zone) in the type of ... and with an accretion rate of ... between ... (figures and units) and ... (figures and units) heights above ... (datum).",
"QFU": "What is the magnetic direction (or number) of the runway to be used? / The magnetic direction (or number) of the runway to be used is ...",
"QFV": "Are the floodlights switched on? / The floodlights are switched on.",
"QFW": "What is the length of the runway in use in ... (units)? / The length of runway ... now in use is ... (figures and units).",
"QFX": "I am working (or am going to work) on a fixed aerial.",
"QFY": "Please report the present meteorological landing conditions [at ... (place)]. / The present meteorological landing conditions at ... (place) are ...",
"QFZ": "What is the aerodrome meteorological forecast for ... (place) for the period ... hours until ... hours? / The aerodrome meteorological forecast for ... (place) for the period ... hours until ... hours is ...",
"QGC": "There are obstructions to the ... of ... runway ...",
"QGD": "Are there on my track any obstructions whose elevation equals or exceeds my altitude? / There are obstructions on your track ... (figures and units) height above ... (datum).",
"QGE": "What is my distance to your station (or to ...)? / Your distance to my station (or to ...) is ... (distance figures and units).",
"QGH": "May I land using ... (procedure or facility)? / You may land using ... (procedure or facility).",
"QGK": "What track should I make good? / Make good a track from ... (place) on ... degrees ... (true or magnetic).",
"QGL": "May I enter the ... (control area or zone) at ... (place)? / You may enter the ... (control area or zone) at ... (place).",
"QGM": "Leave the ... (control area or zone).",
"QGN": "May I be cleared to land [at ... (place)]? / You are cleared to land [at ... (place)].",
"QGO": "Landing is prohibited at ... (place).",
"QGP": "What is my number for landing? / You are number ... to land.",
"QGQ": "May I hold at ... (place)? / Hold at ... (place) at flight level/altitude ... (datum) and await further clearance.",
"QGT": "Fly for ... minutes on a heading what will enable you to maintain a track reciprocal to your present one.",
"QGU": "Fly for ... minutes on a magnetic heading of ... degrees.",
"QGV": "Do you see me? / Can you see the aerodrome? / Can you see ... (aircraft)? / I see you at ... (cardinal or quadrantal point of direction).",
"QGW": "Does my landing gear appear to be down and in place? / Your landing gear appears to be down and in place.",
"QGZ": "Hold on ... direction of ... facility.",
"QHE": "Will you inform me when you are on ... leg of approach? / I am on ...(leg).. of approach.",
"QHG": "May I enter traffic circuit at flight level/altitude ...? / Cleared to enter traffic circuit at flight level/altitude ...",
"QHH": "Are you making an emergency landing? / I am making an emergency landing.",
"QHI": "Are you (or is ...) ... waterborne / on land? ",
"QHQ": "May I make a ... approach [at ... (place)]? / You may make a ... approach [at ... (place)].",
"QHZ": "Shall I circle the aerodrome (or go around)? / Circle the aerodrome (or go around).",
"QIC": "May I establish communication with ... radio station on ... kHz (or ... MHz.) now (or at ... hours)? / Establish communication with ... radio station on ... kHz. (or MHz.) now (or at ...hours).",
"QIF": "What frequency is ... using? / ... is using ... kHz (or ... MHz.).",
"QJA": "Is my tape / mark and space reversed? / Your tape / mark and space is reversed.",
"QJB": "Will you use radio/cable/telegraph/teletype/telephone/receiver/transmitter/reperforator? / I will use ...",
"QJC": "Will you check your transmitter/autohead/perforator/reperfordator/printer/keyboard/antenna? / I will check my ...",
"QJD": "Am I transmitting letters/figures? / You are transmitting ...",
"QJE": "Is my frequency shift wide/narrow/correct? / Your frequency shift is ... (by ... cycles)",
"QJF": "My signal as checked by monitor locally/as radiated is satisfactory.",
"QJG": "Shall I revert to automatic relay? / Revert to automatic relay.",
"QJH": "Shall I run test tape/sentence? / Run test tape/sentence.",
"QJI": "Will you transmit a continuous mark/space? / I am transmitting a continuous mark/space.",
"QJK": "Are you receiving continuous mark/space / mark/space bias? / I am receiving a continuous mark/space / mark/space bias.",
"QKC": "The sea conditions (at ... position) ... permit alighting but not takeoff / render alihting extremely hazardous.",
"QKF": "May I be relieved (at ... hours)? / You may expect to be relieved at ... hours [by aircraft/vessel/callsign/name].",
"QKG": "Will relief take place when ... (identification) establishes visual/comms contact with survivors? / Relief will take place when ... (identification) establishes visual/comms contact with survivors.",
"QKH": "Report details of the parallel sweep (track) search being (or to be) conducted? --or-- In the parallel sweep (track) search being (or to be) conducted, what is (are) the direction/separation/altitude of sweeps employed in the search pattern? / The parallel sweep (track) search is being (or to be) conducted [with direction of sweeps ... degrees ... (true or magnetic) || with ... (distance figures and units) separation between sweeps || flight level/altitude].",
"QKN": "Aircraft plotted (believed to be you) in position ... on track ... degrees at ... hours.",
"QKO": "What other units are (or will be) taking part in the operation [... (identification of operation)]? / In the operation [... (identification)], the following units are (or will be) taking part ... (name of units). --or-- ... (name) unit is taking part in operation [... (identification] (with effect from ... hours).",
"QKP": "Which pattern of search is being followed? / The search pattern is parallel sweep/square search/creeping line ahead/track crawl/contour search/combined search by aircraft and ship/[other].",
"QLB": "Will you monitor ... station and report regarding range, quality, etc.? / I have monitored ... station and report (briefly) as follows ...",
"QLE": "What is your expected signal? / The expected signal is low...",
"QLF": "Are you sending with your left foot? Try sending with your left foot!",
"QLH": "Will you use simultaneous keying on ... frequency and ... frequency? / I will now key simultaneously on ... frequency and ... frequency.",
"QLV": "Is the ... radio facility still required? / The ... radio facility is still required.",
"QMH": "Shift to transmit and receive on ... kHz (or ... MHz.); if communication is not established within 5 minutes, revert to present frequency.",
"QMI": "Report the vertical distribution of cloud [at ... (position or zone)] as observed from your aircraft. / The vertical distribution of cloud as observed from my aircraft at ... hours at ... (position or zone) is : *lowest layer observed* ... eights (... type) with base of ... (figures and units) and tops of ... (figures and units) [*and similarly in sequence for each of the layers observed.] height above ... (datum).",
"QMU": "What is the surface temperature at ... (place) and what is the dew point temperature at that place? / The surface temperature at ... (place) at ... hours is ... degrees and the dew point temperature at that time and place is ... degrees.",
"QMW": "At ... (position or zone) what is (are) the flight level(s)/altitude(s) ... of the zero Celsius isotherm(s)? / At ... (position or zone) the zero Celsius isotherm(s) is (are) at flight level(s)/altitude(s) ...",
"QMX": "What is the air temperature [at ... (position or zone)] (at ... hours) at flight level/altitude ...? / At ... (position or zone) at ... hours the air temperature is ... (degrees and units) at flight level/altitude ... --Note-- Aircraft reporting QMX information will transmit the temperature figures as corrected for airspeed.",
"QMZ": "Have you any amendments to the flight forecast in respect of section of route yet to be traversed? / The following amendment(s) should be made to the flight forecast ... [If no amendments, signal QMZ NIL]",
"QNE": "What indication will my altimeter give on landing at ... (place) at ... hours, my sub-scale being set to 1013.2 millibars (29.92 inches)? / On landing at ... (place) at ... hours, with your sub-scale being set to 1013.2 millibars (29.92 inches), your altimeter will indicate ... (figures and units).",
"QNH": "What should I set on the subscale of my altimeter so that the instrument would indicate its elevation if my aircraft were on the ground at your station? / If you set the subscale of your altimeter to read ... millibars, the instrument would indicate its elevation if your aircraft were on the ground at my station at ... hours. --Note-- When the setting is given in hundredths of inch the abbreviation INS is used to identify the units.",
"QNI": "May I join the net? / You may check in...",
"QNO": "I am not equipped to give the information (or provide the facility) requested.",
"QNR": "I am approaching my point of no return.",
"QNT": "What is the maximum speed of the surface wind at ... (place)? / The maximum speed of the surface wind at ... (place) at ... hours is ... (speed figures and units).",
"QNY": "What is the present weather and the intensity thereof at ... (place, position or zone)? / The present weather and intensity thereof at ... (place, position or zone) at ... hours is ...",
"QOA": "Can you communicate by radiotelegraphy (500 kHz)? / I can communicate by radiotelegraphy (500 kHz).",
"QOB": "Can you communicate by radiotelephony (2182 kHz)? / I can communicate by radiotelephony (2182 kHz).",
"QOC": "Can you communicate by radiotelephony (channel 16 - frequency 156.80 MHz)? / I can communicate by radiotelephony (channel 16 - frequency 156.80 MHz).",
"QOD": "Can you communicate with me in Dutch/English/French/German/Greek/Italian/Japanese/Norwegian/Russian/Spanish? / I can communicate with you in ...",
"QOE": "Have you received the safety signal sent by ... (name and/or call sign)? / I have received the safety signal sent by ... (name and/or call sign).",
"QOF": "What is the commercial quality of my signals? / The quality of your signals is not commercial/marginally commercial/commercial.",
"QOG": "How many tapes have you to send? / I have ... tapes to send.",
"QOH": "Shall I send a phasing signal for ... seconds? / Send a phasing signal for ... seconds.",
"QOI": "Shall I send my tape? / Send your tape.",
"QOJ": "Will you listen on ... kHz (or MHz) for signals of emergency position-indicating radiobeacons? / I am listening on ... kHz (or MHz) for signals of emergency position-indicating radiobeacons.",
"QOK": "Have you received the signals of an emergency position-indicating radiobeacon on ... kHz (or MHz)? / I have received the signals of an emergency position-indicating radiobeacon on ... kHz (or MHz).",
"QOL": "Is your vessel fitted for reception of selective calls? If so, what is your selective call number or signal? / My vessel is fitted for the reception of selective calls. My selective call number or signal is ...",
"QOM": "On what frequencies can your vessel be reached by a selective call? / My vessel can be reached by a selective call on the following frequency/ies ... (periods of time to be added if necessary).",
"QOO": "Can you send on any working frequency? / I can send on any working frequency.",
"QOT": "Do you hear my call; what is the approximate delay in minutes before we may exchange traffic? / I hear your call; the approximate delay is ... minutes.",
"QRA": "What is the name of your vessel (or station)? / The name of my vessel (or station) is ...",
"QRB": "How far approximately are you from my station? / The approximate distance between our stations is ... nautical miles (or km).",
"QRC": "What is your true bearing? / My true bearing is ____ degrees.",
"QRD": "Where are you bound for? / I am bound for ____.",
"QRE": "What is your estimated time of arrival at ... (or over ...) (place)? / My estimated time of arrival at ... (or over ...) (place) is ... hours.",
"QRF": "Where are you bound from? / I am bound from ____.",
"QRG": "Will you tell me my exact frequency (or that of ...)? / Your exact frequency (or that of ...) is ... kHz (or MHz).",
"QRH": "Does my frequency vary? / Your frequency varies.",
"QRI": "How is the tone of my transmission? / The tone of your transmission is ...",
"QRJ": "How many voice contacts do you want to make? / I want to make ... voice contacts.",
"QRK": "How do you receive me? / I am receiving (1-5).",
"QRL": "Are you busy? / I am busy (or I am busy with ...). Please do not interfere.",
"QRM": "Are you being interfered with? / I am being interfered with.",
"QRN": "Are the atmospherics strong? / Atmospherics (noise) are very strong.",
"QRO": "Shall I increase transmitter power? / Increase transmitter power.",
"QRP": "Shall I decrease transmitter power? / Decrease transmitter power.",
"QRQ": "Shall I send faster? / Send faster (... wpm)",
"QRR": "Are you ready for automatic operation? / I am ready for automatic operation. Send at ... words per minute.",
"QRS": "Shall I send more slowly? / Send more slowly (... words per minute).",
"QRT": "Shall I stop sending? / Stop sending.",
"QRU": "Have you anything for me? / I have nothing for you.",
"QRV": "Are you ready? / I am ready.",
"QRW": "Shall I inform ... that you are calling him on ... kHz (or MHz)? / Please inform ... that I am calling him on ... kHz (or MHz).",
"QRX": "When will you call me again? / I will call you again at ... hours (on ... kHz (or MHz)).",
"QRY": "What is my turn? / Your turn is Number ... (or according to any other indication).",
"QRZ": "Who is calling me? / You are being called by ... (on ... kHz (or MHz)).",
"QSA": "What is the strength of my signals (or those of ...)? / The strength of your signals (or those of ...) is ...",
"QSB": "Are my signals fading? / Your signals are fading.",
"QSC": "Are you a cargo vessel? --or-- Are you a low traffic ship? / I am a cargo vessel. --or-- I am a low traffic ship.",
"QSD": "Is my keying defective? --or-- Are my signals mutilated? / Your keying is defective. --or-- Your signals are mutilated.",
"QSE": "What is the estimated drift of the survival craft? / The estimated drift of the survival craft is ... (figures and units).",
"QSF": "Have you effected rescue? / I have effected rescue and am proceeding to ... base (with ... persons injured requiring ambulance).",
"QSG": "Shall I send ... telegrams at a time? / Send ... telegrams at a time.",
"QSH": "Are you able to home on your direction-finding equipment? / I am able to home on my D/F equipment (on station ...).",
"QSI": "I have been unable to break in on your transmission. --or-- Will you inform ... (call sign) that I have been unable to break in on his transmission (on ... kHz (or MHz)).",
"QSJ": "What is the charge to be collected to ... including your internal charge? / The charge to be collected to ... including my internal charge is ... francs.",
"QSK": "Can you hear me between your signals and if so can I break in on your transmission? / I can hear you between my signals; break in on my transmission.",
"QSL": "Can you acknowledge receipt? / I am acknowledging receipt.",
"QSM": "Shall I repeat the last telegram (message) which I sent you, or some previous telegram (message)? / Repeat the last telegram (message) which you sent me (or telegram(s) (message(s)) numbers(s) ...).",
"QSN": "Did you hear me (or ... (call sign)) on ... kHz (or MHz)? / I did hear you (or ... (call sign)) on ... kHz (or MHz).",
"QSO": "Can you communicate with ... direct (or by relay)? / I can communicate with ... direct (or by relay through ...).",
"QSP": "Will you relay to ... free of charge? / I will relay to ... free of charge.",
"QSQ": "Have you a doctor on board (or is ... (name of person) on board)? / I have a doctor on board (or ... (name of person) is on board).",
"QSR": "Shall I repeat the call on the calling frequency? / Repeat your call on the calling frequency; did not hear you (or have interference).",
"QSS": "What working frequency will you use? / I will use the working frequency ... kHz (or MHz) (in the HF bands normally only the last three figures of the frequency need be given).",
"QST": "Here is a broadcast message to all amateurs.",
"QSU": "Shall I send or reply on this frequency (or on ... kHz (or MHz)) (with emissions of class ...)? / Send or reply on this frequency (or on ... kHz (or MHz)) (with emissions of class ...).",
"QSV": "Shall I send a series of Vs on this frequency (or on ... kHz (or MHz))? / Send a series of Vs on this frequency (or on ... kHz (or MHz)).",
"QSW": "Will you send on this frequency (or on ... kHz (or MHz)) (with emissions of class ...)? / I am going to send on this frequency (or on ... kHz (or MHz)) (with emissions of class ...).",
"QSX": "Will you listen to ... (call sign(s)) on ... kHz (or MHz)? --or-- Will you listen to ... (call sign(s)) on ... kHz (or MHz), or in the bands ... / channels ... ? / I am listening to ... (call sign(s)) on ... kHz (or MHz). --or-- I am listening to ... (call sign(s)) on ... kHz (or MHz), or in the bands ... / channels ...",
"QSY": "Shall I change to transmission on another frequency? / Change to transmission on another frequency (or on ... kHz (or MHz)).",
"QSZ": "Shall I send each word or group more than once? / Send each word or group twice (or ... times).",
"QTA": "Shall I cancel telegram (or message) number ... ? / Cancel telegram (or message) number ...",
"QTB": "Do you agree with my counting of words? / I do not agree with your counting of words; I will repeat the first letter or digit of each word or group.",
"QTC": "How many telegrams have you to send? / I have ... telegrams for you (or for ...).",
"QTD": "What has the rescue vessel or rescue aircraft recovered? / ... (identification) has recovered ... survivors/wreckage/bodies.",
"QTE": "What is my TRUE bearing from you? --or-- What is my TRUE bearing from ... (call sign)? --or-- What is the TRUE bearing of ... (call sign) from ... (call sign)? / Your TRUE bearing from me is ... degrees at ... hours. --or-- Your TRUE bearing from ... (call sign) was ... degrees at ... hours. --or-- The TRUE bearing of ... (call sign) from ... (call sign) was ... degrees at ... hours.",
"QTF": "Will you give me the position of my station according to the bearings taken by the direction-finding stations which you control? / The position of your station according to the bearings taken by the D/F stations which I control was ... latitude, ... longitude (or other indication of position), class ... at ... hours.",
"QTG": "Will you send two dashes of ten seconds each followed by your call sign (repeated ... times) (on ... kHz (or MHz))? --or-- Will you request ... to send two dashes of ten seconds followed by his call sign (repeated ... times) on ... kHz (or MHz)? / I am going to send two dashes of ten seconds each followed by my call sign (repeated ... times) (on ... kHz (or MHz)). --or-- I have requested ... to send two dashes of ten seconds followed by his call sign (repeated ... times) on ... kHz (or MHz).",
"QTHR": "At the registered location; Chiefly British in voice or writing, Historically - the location in the printed Callbook. Modernly - as given in online government records for my callsign",
"QTH": "What is your position in latitude and longitude (or according to any other indication)? / My position is ... latitude, ... longitude (or according to any other indication).",
"QTI": "What is your TRUE course? / My TRUE course is ... degrees.",
"QTJ": "What is your speed? / My speed is ... knots (or ... kilometres per hour or ... statute miles per hour).",
"QTK": "What is the speed of your aircraft in relation to the surface of the Earth? / The speed of my aircraft in relation to the surface of the Earth is ... knots (or ... kilometres per hour or ... statute miles per hour).",
"QTL": "What is your TRUE heading? / My TRUE heading is ... degrees.",
"QTM": "What is your MAGNETIC heading? / My MAGNETIC heading is ... degrees.",
"QTN": "At what time did you depart from ... (place)? / I departed from ... (place) at ... hours.",
"QTO": "Have you left dock (or port)? --or-- Are you airborne? / I have left dock (or port). --or-- I am airborne.",
"QTP": "Are you going to enter dock (or port)? --or-- Are you going to alight (or land)? / I am going to enter dock (or port). --or-- I am going to alight (or land).",
"QTQ": "Can you communicate with my station by means of the International Code of Signals (INTERCO)? / I am going to communicate with your station by means of the International Code of Signals (INTERCO).",
"QTR": "What is the correct time? / The correct time is ... hours.",
"QTS": "Will you send your call sign (and/or name) for ... seconds? / I will send my call sign (and/or name) for ... seconds.",
"QTT": "The identification signal which follows is superimposed on another transmission.",
"QTU": "What are the hours during which your station is open? / My station is open from ... to ... hours.",
"QTV": "Shall I stand guard for you on the frequency of ... kHz (or MHz) (from ... to ... hours)? / Stand guard for me on the frequency of ... kHz (or MHz) (from ... to ... hours).",
"QTW": "What is the condition of survivors? / Survivors are in ... condition and urgently need ...",
"QTX": "Will you keep your station open for further communication with me until further notice (or until ... hours)? / I will keep my station open for further communication with you until further notice (or until ... hours).",
"QTY": "Are you proceeding to the position of incident and if so when do you expect to arrive? / I am proceeding to the position of incident and expect to arrive at ... hours (on ... (date)).",
"QTZ": "Are you continuing the search? / I am continuing the search for ... (aircraft, ship, survival craft, survivors or wreckage).",
"QUA": "Have you news of ... (call sign)? / Here is news of ... (call sign).",
"QUB": "Can you give me in the following order information concerning: the direction in degrees TRUE and speed of the surface wind; visibility; present weather; and amount, type and height of base of cloud above surface elevation at ... (place of observation)? / Here is the information requested: ... (The units used for speed and distances should be indicated.)",
"QUC": "What is the number (or other indication) of the last message you received from me (or from ... (call sign))? / The number (or other indication) of the last message I received from you (or from ... (call sign)) is ...",
"QUD": "Have you received the urgency signal sent by ... (call sign of mobile station)? / I have received the urgency signal sent by ... (call sign of mobile station) at ... hours.",
"QUE": "Can you speak in ... (language), - with interpreter if necessary; if so, on what frequencies? / I can speak in ... (language) on ... kHz (or MHz).",
"QUF": "Have you received the distress signal sent by ... (call sign of mobile station)? / I have received the distress signal sent by ... (call sign of mobile station) at ... hours.",
"QUG": "Will you be forced to alight (or land)? / I am forced to alight (or land) immediately. --or-- I shall be forced to alight (or land) at ... (position or place) at ... hours.",
"QUH": "Will you give me the present barometric pressure at sea level? / The present barometric pressure at sea level is ... (units).",
"QUI": "Are your navigation lights working? / My navigation lights are working.",
"QUJ": "Will you indicate the TRUE track to reach you (or ...)? / The TRUE track to reach me (or ...) is ... degrees at ... hours.",
"QUK": "Can you tell me the condition of the sea observed at ... (place or coordinates)? / The sea at ... (place or coordinates) is ...",
"QUL": "Can you tell me the swell observed at ... (place or coordinates)? / The swell at ... (place or coordinates) is ...",
"QUM": "May I resume normal working? / Normal working may be resumed.",
"QUN": "When directed to all stations: Will vessels in my immediate vicinity ... (or in the vicinity of ... latitude, ... longitude) please indicate their position, TRUE course and speed? --or-- When directed to a single station: please indicate their position, TRUE course and speed? / My position, TRUE course and speed are ...",
"QUO": "Shall I search for aircraft/ship/survival craft in the vicinity of ... latitude, ... longitude (or according to any other indication)? / Please search for aircraft/ship/survival craft in the vicinity of ... latitude, ... longitude (or according to any other indication).",
"QUP": "Will you indicate your position by searchlight/black smoke trail/pyrotechnic lights? / My position is indicated by searchlight/black smoke trail/pyrotechnic lights.",
"QUQ": "Shall I train my searchlight nearly vertical on a cloud, occulting if possible and, if your aircraft is seen, deflect the beam up wind and on the water (or land) to facilitate your landing? / Please train your searchlight on a cloud, occulting if possible and, if my aircraft is seen or heard, deflect the beam up wind and on the water (or land) to facilitate my landing.",
"QUR": "Have survivors received survival equipment/been picked up by rescue vessel/been reached by ground rescue party? / Survivors are in possession of survival equipment dropped by ... / have been picked up by rescue vessel/have been reached by ground rescue party.",
"QUS": "Have you sighted survivors or wreckage? / If so, in what position? / Have sighted ... survivors in water/survivors on rafts/wreckage in position ... latitude, ... longitude (or according to any other indication).",
"QUT": "Is position of incident marked? / Position of incident is marked by flame or smoke float/sea marker/sea marker dye/... (specify other marking).",
"QUU": "Shall I home ship or aircraft to my position? / Home ship or aircraft ... (call sign) ... to your position by transmitting your call sign and long dashes on ... kHz (or MHz)/by transmitting on ... kHz (or MHz) TRUE track to reach you.",
"QUW": "Are you in the search area designated as ... (designator or latitude and longitude)? / I am in the ... (designation) search area.",
"QUX": "Do you have any navigational warnings or gale warnings in force? / I have the following navigational warning(s) or gale warning(s) in force: ...",
"QUY": "Is position of survival craft marked? / Position of survival craft was marked at ... hours by flame or smoke float/sea marker/sea marker dye/... (specify other marking).",
"QUZ": "May I resume restricted working? / Distress phase still in force; restricted working may be resumed.",
"QZZ": "Daily key change about to take place (German WWII usage)."}
+23 -2
View File
@@ -29,5 +29,26 @@ owners_uids = (200102491231092736,)
# The extensions to load when running the bot. # The extensions to load when running the bot.
exts = ['ae7q', 'base', 'fun', 'grid', 'ham', 'image', 'lookup', 'morse', 'qrz', 'study', 'weather'] exts = ['ae7q', 'base', 'fun', 'grid', 'ham', 'image', 'lookup', 'morse', 'qrz', 'study', 'weather']
# The text to put in the "playing" status. # Either "time", "random", or "fixed" (first item in statuses)
game = 'with lids on 7.200' status_mode = "fixed"
# Random statuses pool
statuses = ["with lids on the air", "with fire"]
# Timezone for the status (string)
# See https://pythonhosted.org/pytz/ for more info
status_tz = 'US/Eastern'
# The text to put in the "playing" status, with start and stop times
time_statuses = [('with lids on 3.840', (00, 00), (6, 00)),
('with lids on 7.200', (6, 00), (10, 00)),
('with lids on 14.313', (10, 00), (18, 00)),
('with lids on 7.200', (18, 00), (20, 00)),
('with lids on 3.840', (20, 00), (23, 59))]
# Emoji IDs and keywords for emoji reactions
# Use the format {emoji_id (int): ('tuple', 'of', 'lowercase', 'keywords')}
msg_reacts = {}
# A :pika: emote's ID, None for no emote :c
pika = 658733876176355338