Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e35e8949a | |||
| 77b572eb3e | |||
| be042a9641 | |||
| 488ae6cc98 | |||
| a65fd04dbd | |||
| 6329718d29 | |||
| 756a15c4c5 | |||
| b462527211 | |||
| 93b42e64dc | |||
| f6103ef6f1 | |||
| 090b96482d | |||
| 6d71974ea1 | |||
| 0af82ac241 | |||
| 2ba6249b90 | |||
| 2c11dad358 | |||
| 5f796d479e | |||
| 3ba55d4c35 | |||
| f4ed93dc76 | |||
| 2cb4b03532 | |||
| bc93462c29 | |||
| 6867c45c8c | |||
| dcbb7acab8 | |||
| 3803ce6045 | |||
| 8ca4911072 | |||
| 6e2468f04f |
@@ -0,0 +1,29 @@
|
||||
### Description
|
||||
|
||||
*Describe the changes you made here.*
|
||||
|
||||
Fixes #{issue}
|
||||
|
||||
### Type of change
|
||||
|
||||
Please delete options that are not relevant.
|
||||
|
||||
- Bug fix (non-breaking change which fixes an issue)
|
||||
- New feature (non-breaking change which adds functionality)
|
||||
- Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
||||
- This change requires a documentation update
|
||||
|
||||
### How has this been tested?
|
||||
|
||||
*Describe the procedure used for verifying your changes here.*
|
||||
|
||||
### Checklist
|
||||
|
||||
- [ ] Issue exists for PR
|
||||
- [ ] Code reviewed by the author
|
||||
- [ ] Code documented (comments or other documentation)
|
||||
- [ ] Changes tested
|
||||
- [ ] `flake8` passes
|
||||
- [ ] `CHANGELOG.md` updated
|
||||
- [ ] Informative commit messages
|
||||
- [ ] Descriptive PR title
|
||||
@@ -0,0 +1,58 @@
|
||||
name: Docker Build and Push
|
||||
|
||||
on:
|
||||
push:
|
||||
# Publish `master` as Docker `dev` image.
|
||||
branches:
|
||||
- master
|
||||
# Publish `v*` tags as releases and as `latest`.
|
||||
tags:
|
||||
- v*
|
||||
|
||||
env:
|
||||
IMAGE_NAME: qrm2
|
||||
|
||||
jobs:
|
||||
build-and-push:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Build image
|
||||
run: |
|
||||
echo ${{ github.sha }} > git_commit
|
||||
docker build . --file Dockerfile -t $IMAGE_NAME
|
||||
|
||||
- name: Log into Github Package Registry
|
||||
run: echo "${{ secrets.GITHUB_TOKEN }}" | docker login docker.pkg.github.com -u ${{ github.actor }} --password-stdin
|
||||
|
||||
- name: Log into Docker Hub
|
||||
run: echo "${{ secrets.DOCKER_PASSWORD }}" | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin
|
||||
|
||||
- name: Push image to registries
|
||||
run: |
|
||||
GITHUB_IMAGE_ID=docker.pkg.github.com/${{ github.repository }}/$IMAGE_NAME
|
||||
DOCKER_IMAGE_ID=${{ secrets.DOCKER_USERNAME }}/$IMAGE_NAME
|
||||
|
||||
# Strip git ref prefix from version
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
|
||||
# Strip "v" prefix from tag name
|
||||
[[ "${{ github.ref }}" == "refs/tags/"* ]] && VERSION=$(echo $VERSION | sed -e 's/^v//')
|
||||
|
||||
[[ "$VERSION" == "master" ]] && VERSION=dev
|
||||
|
||||
echo GITHUB_IMAGE_ID=$GITHUB_IMAGE_ID
|
||||
echo DOCKER_IMAGE_ID=$DOCKER_IMAGE_ID
|
||||
echo VERSION=$VERSION
|
||||
|
||||
# tag for Github Registry
|
||||
docker tag $IMAGE_NAME $GITHUB_IMAGE_ID:$VERSION
|
||||
[[ "$VERSION" != "dev" ]] && docker tag $IMAGE_NAME $GITHUB_IMAGE_ID:latest
|
||||
docker push $GITHUB_IMAGE_ID
|
||||
|
||||
# tag for Docker Hub
|
||||
docker tag $IMAGE_NAME $DOCKER_IMAGE_ID:$VERSION
|
||||
[[ "$VERSION" != "dev" ]] && docker tag $IMAGE_NAME $DOCKER_IMAGE_ID:latest
|
||||
docker push $DOCKER_IMAGE_ID
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Linting
|
||||
|
||||
on: [push]
|
||||
on: [push,pull_request]
|
||||
|
||||
jobs:
|
||||
flake8_py3:
|
||||
|
||||
@@ -9,6 +9,8 @@ cty.zip
|
||||
|
||||
/docker-compose.yml
|
||||
|
||||
.vscode/
|
||||
|
||||
|
||||
#########################################################
|
||||
# Byte-compiled / optimized / DLL files
|
||||
|
||||
@@ -7,6 +7,43 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
## [2.4.0] - 2020-09-27
|
||||
### Added
|
||||
- Canadian prefix info to the `?prefixes` command.
|
||||
- `?worksplit` command.
|
||||
- Maps for CQ Zones, ITU Zones, ITU Regions, and Canadian prefixes.
|
||||
- Attribution for all maps.
|
||||
- Option to append ` | ?help` to the playing status.
|
||||
- `?dbconv` command to convert voltage, power, and antenna gain values.
|
||||
### Changed
|
||||
- ARRL/RAC section maps to include all current ARRL/RAC sections.
|
||||
### Fixed
|
||||
- Issue where multiple prefixes were not handled properly.
|
||||
|
||||
|
||||
## [2.3.2] - 2020-07-22
|
||||
### Fixed
|
||||
- Dependency issues
|
||||
|
||||
|
||||
## [2.3.1] - 2020-04-02
|
||||
### Fixed
|
||||
- Wordlist containing innappropriate words.
|
||||
|
||||
|
||||
## [2.3.0] - 2020-03-30
|
||||
### Added
|
||||
- `?phoneticweight` command, which calculates a message's length in syllables.
|
||||
- `?standards` command to display [xkcd 927](https://xkcd.com/927/).
|
||||
### Changed
|
||||
- Python>=3.7 now required.
|
||||
|
||||
|
||||
## [2.2.3] - 2020-03-29
|
||||
### Fixed
|
||||
- Commands are no longer case-sensitive.
|
||||
|
||||
|
||||
## [2.2.2] - 2020-02-25
|
||||
### Fixed
|
||||
- Fixed issue where HamStudy questions with images would cause an error.
|
||||
@@ -87,7 +124,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
## 1.0.0 - 2019-07-31 [YANKED]
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/miaowware/qrm2/compare/v2.2.2...HEAD
|
||||
[Unreleased]: https://github.com/miaowware/qrm2/compare/v2.4.0...HEAD
|
||||
[2.4.0]: https://github.com/miaowware/qrm2/releases/tag/v2.4.0
|
||||
[2.3.2]: https://github.com/miaowware/qrm2/releases/tag/v2.3.2
|
||||
[2.3.1]: https://github.com/miaowware/qrm2/releases/tag/v2.3.1
|
||||
[2.3.0]: https://github.com/miaowware/qrm2/releases/tag/v2.3.0
|
||||
[2.2.3]: https://github.com/miaowware/qrm2/releases/tag/v2.2.3
|
||||
[2.2.2]: https://github.com/miaowware/qrm2/releases/tag/v2.2.2
|
||||
[2.2.1]: https://github.com/miaowware/qrm2/releases/tag/v2.2.1
|
||||
[2.2.0]: https://github.com/miaowware/qrm2/releases/tag/v2.2.0
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
# Contributing to qrm
|
||||
|
||||
## Before You Start
|
||||
|
||||
- Make sure there's an issue for the feature, bugfix, or other improvement you want to make.
|
||||
- Make sure it's something that the project maintainers want.
|
||||
We can discuss it and assign the issue to you.
|
||||
- Make sure work isn't already being done on the issue.
|
||||
|
||||
### Environment Setup
|
||||
|
||||
Once all of the above is done, you can get started by setting up your development envronment.
|
||||
|
||||
1. [Fork this repo][1] into your own GitHub namespace.
|
||||
1. Make sure the `master` branch is up to date, then make yourself a new branch with a descriptive name.
|
||||
1. Once the forked repo is cloned and on the proper branch, you can set up the development environment.
|
||||
1. Install python 3.7 or higher.
|
||||
1. Run `make dev-install`.
|
||||
This should install everything you need to develop and run qrm.
|
||||
1. [Create a bot and token][2], and add it to `data/keys.py`.
|
||||
Also add your [QRZ credentials][3] if needed.
|
||||
1. In `data/options.py`, change values as needed.
|
||||
Some commands require adding your Discord user ID to `owner_uids`.
|
||||
1. To activate the virtual env that was created by `make`, run `source botenv/bin/activate` (or the equivelent for your shell or operating system).
|
||||
|
||||
## While You Develop
|
||||
|
||||
To run qrm, use the command `./run.sh`.
|
||||
We recommend you use the `--pass-errors` flags to avoid perpetual restart loops if you break the bot.
|
||||
It exists because repeatedly mashing [Ctrl+C] at high speed to break a fast loop is not fun.
|
||||
|
||||
Commit messages should be descriptive and mention issues that they fix ("fixes #123") or contain progress on ("progress on #123").
|
||||
Make commits as needed, but try to keep it reasonable.
|
||||
If there are too many, your contribution may be squashed when merged.
|
||||
You may want to squash your commits locally yourself:
|
||||
|
||||
```sh
|
||||
git reset --soft [commit before your changes]
|
||||
git commit
|
||||
```
|
||||
|
||||
Make sure to document your code as you go, in both comments and external documentation (in `/dev-notes/`) as needed.
|
||||
`dev-notes` is especially important if you introduce a new json file format or to document some development process (like the command to crush the various images in the repository).
|
||||
|
||||
**Test your changes.**
|
||||
If your code doesn't work, it's not ready for merging.
|
||||
Make sure you not only test intended behaviour, but also edge cases and error cases.
|
||||
Make sure to run `flake8` to ensure your code uses the proper style, and `mypy [files...]` to ensure proper typing.
|
||||
|
||||
If you're making a user-facing change, put a quick summary in `CHANGELOG.md` under the `[Unreleased]` heading.
|
||||
Follow the [Keep a Changelog][4] format.
|
||||
|
||||
### A Note on Style
|
||||
|
||||
qrm tries to keep to PEP 8 style whenever possible.
|
||||
Use the utility `flake8` to check that you follow this style.
|
||||
When you start a PR or push commits, GitHub will automatically run this for you;
|
||||
if that fails, you will be expected to fix those errors before merge.
|
||||
|
||||
Otherwise, try to follow the existing style:
|
||||
double-quotes except when required to be single,
|
||||
indentation of mult-line structures matching other examples in the code,
|
||||
add type hints,
|
||||
etc.
|
||||
|
||||
## When You're Ready to Merge
|
||||
|
||||
1. When you have finished working on your contribution, create a pull request from your fork's branch into the master branch of this repository.
|
||||
1. Read through and complete the pull request template.
|
||||
If the checklist is not complete, your contribution will not be merged.
|
||||
1. Your pull request will get reviewed by at least one maintainer.
|
||||
1. If approved, another maintainer may merge the pull request if everything looks good.
|
||||
|
||||
[1]: https://github.com/miaowware/qrm2/fork
|
||||
[2]: https://discordpy.readthedocs.io/en/latest/discord.html
|
||||
[3]: https://www.qrz.com/page/xml_data.html
|
||||
[4]: https://keepachangelog.com/en/1.0.0/
|
||||
@@ -71,6 +71,17 @@ clean:
|
||||
|
||||
|
||||
### Dev targets ###
|
||||
.PHONY: dev-install
|
||||
dev-install: $(BOTENV)/dev_req_done data/options.py data/keys.py
|
||||
|
||||
# Installing dev requirements
|
||||
$(BOTENV)/dev_req_done: dev-requirements.txt $(BOTENV)/success
|
||||
@echo "\033[34;1m--> Installing the dependencies...\033[0m"
|
||||
@. $(BOTENV)/bin/activate; \
|
||||
pip install ${PIP_OUTPUT} -U pip setuptools wheel; \
|
||||
pip install ${PIP_OUTPUT} -U -r dev-requirements.txt
|
||||
@touch $(BOTENV)/dev_req_done
|
||||
|
||||
|
||||
|
||||
### Special targets ###
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Docker help for qrm2
|
||||
|
||||
You have multiple ways to use docker to run an instance of qrm2
|
||||
You have multiple ways to use docker to run an instance of qrm2.
|
||||
|
||||
- [Docker help for qrm2](#docker-help-for-qrm2)
|
||||
- [Using docker-compose and the prebuilt-image (recommended)](#using-docker-compose-and-the-prebuilt-image-recommended)
|
||||
@@ -23,13 +23,16 @@ This is the easiest method for running the bot without any modifications.
|
||||
version: '3'
|
||||
services:
|
||||
qrm2:
|
||||
image: "classabbyamp/discord-qrm2:latest"
|
||||
image: "classabbyamp/qrm2:latest"
|
||||
# OR
|
||||
# image: "docker.pkg.github.com/miaowware/qrm2/qrm2:latest"
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- "./data:/app/data:rw"
|
||||
environment:
|
||||
- PYTHONUNBUFFERED=1
|
||||
```
|
||||
> Note that there are two possible sources for the image: docker's and github's registry. Github's registry requires [a few extra steps](https://docs.github.com/en/packages/using-github-packages-with-your-projects-ecosystem/configuring-docker-for-use-with-github-packages) during the initial setup.
|
||||
|
||||
3. Create a subdirectory named `data`.
|
||||
|
||||
@@ -59,7 +62,7 @@ This is the easiest method to run the bot with modifications.
|
||||
services:
|
||||
qrm2:
|
||||
build: .
|
||||
image: "discord-qrm2:local-latest"
|
||||
image: "qrm2:local-latest"
|
||||
restart: on-failure
|
||||
volumes:
|
||||
- "./data:/app/data:rw"
|
||||
@@ -75,7 +78,7 @@ This is the easiest method to run the bot with modifications.
|
||||
|
||||
```none
|
||||
$ docker-compose build --pull
|
||||
$ docker-compose -d
|
||||
$ docker-compose up -d
|
||||
```
|
||||
|
||||
> Run without "-d" to test the bot. (run in foreground)
|
||||
@@ -95,7 +98,7 @@ This methods is not very nice to use.
|
||||
2. Run docker build:
|
||||
|
||||
```none
|
||||
$ docker build -t discord-qrm2:local-latest .
|
||||
$ docker build -t qrm2:local-latest .
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -14,6 +14,8 @@ See [README-DOCKER.md](./README-DOCKER.md)
|
||||
|
||||
### Without Docker
|
||||
|
||||
Requires Python 3.7 or newer.
|
||||
|
||||
Prep the environment. For more information on extra options, see the [quick-bot-no-pain Makefile documentation](https://github.com/0x5c/quick-bot-no-pain/blob/master/docs/makefile.md).
|
||||
|
||||
```
|
||||
@@ -26,6 +28,10 @@ Run. For more information on options, see the [quick-bot-no-pain run.sh document
|
||||
$ run.sh
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
Check out the [contribution guidelines](/CONTRIBUTING.md) for more information about how to contribute to this project.
|
||||
|
||||
## Copyright
|
||||
|
||||
Copyright (C) 2019-2020 Abigail Gold, 0x5c
|
||||
|
||||
@@ -63,6 +63,7 @@ emojis = SimpleNamespace(
|
||||
paths = SimpleNamespace(
|
||||
data=Path("./data/"),
|
||||
resources=Path("./resources/"),
|
||||
img=Path("./resources/img/"),
|
||||
bandcharts=Path("./resources/img/bandcharts/"),
|
||||
maps=Path("./resources/img/maps/"),
|
||||
)
|
||||
@@ -70,6 +71,16 @@ paths = SimpleNamespace(
|
||||
|
||||
# --- Classes ---
|
||||
|
||||
|
||||
class CallsignInfoData:
|
||||
"""Represents a country's callsign info"""
|
||||
def __init__(self, data: list):
|
||||
self.title: str = data[0]
|
||||
self.desc: str = data[1]
|
||||
self.calls: str = data[2]
|
||||
self.emoji: str = data[3]
|
||||
|
||||
|
||||
class ImageMetadata:
|
||||
"""Represents the metadata of a single image."""
|
||||
def __init__(self, metadata: list):
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
# Image processing instructions
|
||||
|
||||
For images like bandplans and maps, first resize the image to a reasonable size, then run `pngquant --quality 30-40` on the images.
|
||||
Do not apply that to non-flat images like actual pictures.
|
||||
@@ -20,5 +20,5 @@ Used for grouping info such as name, description, source, and such.
|
||||
| `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)` |
|
||||
| `source` | The source of the file. | `Instituto Federal de Telecomunicaciones (IFT)` |
|
||||
| `emoji` | A Unicode emoji associated with the file. | `📻`, `🇨🇦` |
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
-r requirements.txt
|
||||
flake8
|
||||
discord.py-stubs
|
||||
@@ -29,7 +29,7 @@ class AE7QCog(commands.Cog):
|
||||
self.bot = bot
|
||||
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
|
||||
|
||||
@commands.group(name="ae7q", aliases=["ae"], category=cmn.cat.lookup)
|
||||
@commands.group(name="ae7q", aliases=["ae"], case_insensitive=True, category=cmn.cat.lookup)
|
||||
async def _ae7q_lookup(self, ctx: commands.Context):
|
||||
"""Looks up a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/)."""
|
||||
if ctx.invoked_subcommand is None:
|
||||
|
||||
@@ -10,7 +10,6 @@ the GNU General Public License, version 2.
|
||||
|
||||
import random
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from typing import Union
|
||||
|
||||
import discord
|
||||
@@ -47,9 +46,9 @@ class QrmHelpCommand(commands.HelpCommand):
|
||||
if parent:
|
||||
fmt = f"{parent} {fmt}"
|
||||
alias = fmt
|
||||
return f"{opt.prefix}{alias} {command.signature}\n *Aliases:* {aliases}"
|
||||
return f"{opt.display_prefix}{alias} {command.signature}\n *Aliases:* {aliases}"
|
||||
alias = command.name if not parent else f"{parent} {command.name}"
|
||||
return f"{opt.prefix}{alias} {command.signature}"
|
||||
return f"{opt.display_prefix}{alias} {command.signature}"
|
||||
|
||||
async def send_error_message(self, error):
|
||||
embed = cmn.embed_factory(self.context)
|
||||
@@ -61,7 +60,7 @@ class QrmHelpCommand(commands.HelpCommand):
|
||||
async def send_bot_help(self, mapping):
|
||||
embed = cmn.embed_factory(self.context)
|
||||
embed.title = "qrm Help"
|
||||
embed.description = (f"For command-specific help and usage, use `{opt.prefix}help [command name]`."
|
||||
embed.description = (f"For command-specific help and usage, use `{opt.display_prefix}help [command name]`."
|
||||
" Many commands have shorter aliases.")
|
||||
mapping = await mapping
|
||||
|
||||
@@ -189,7 +188,7 @@ class BaseCog(commands.Cog):
|
||||
|
||||
|
||||
def parse_changelog():
|
||||
changelog = OrderedDict()
|
||||
changelog = {}
|
||||
ver = ""
|
||||
heading = ""
|
||||
|
||||
|
||||
@@ -0,0 +1,201 @@
|
||||
"""
|
||||
Conversion extension for qrm
|
||||
---
|
||||
Copyright (C) 2020 Abigail Gold, 0x5c
|
||||
|
||||
This file is part of qrm2 and is released under the terms of
|
||||
the GNU General Public License, version 2.
|
||||
"""
|
||||
|
||||
|
||||
import math
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
|
||||
import discord.ext.commands as commands
|
||||
|
||||
import common as cmn
|
||||
from data import options as opt
|
||||
|
||||
|
||||
# not sure why but UnitConverter and Unit need to be defined before DbConvCog and convert()
|
||||
class UnitConverter(commands.Converter):
|
||||
async def convert(self, ctx: commands.Context, argument: str):
|
||||
try:
|
||||
return Unit(argument)
|
||||
except ValueError as e:
|
||||
raise commands.BadArgument(message=str(e))
|
||||
|
||||
|
||||
class Unit:
|
||||
def __init__(self, raw: str):
|
||||
self.raw: str = raw
|
||||
self.unit: str
|
||||
self.type: UnitType
|
||||
self.is_db: bool
|
||||
self.mult: int
|
||||
self._parse()
|
||||
|
||||
def _parse(self):
|
||||
s = self.raw.lower()
|
||||
if len(s) > 2 and s[:2] == "db":
|
||||
self.is_db = True
|
||||
if s[2:] in units:
|
||||
u = units[s[2:]]
|
||||
self.mult = u["mult"]
|
||||
self.unit = u["log"]
|
||||
self.type = u["type"]
|
||||
elif s in units:
|
||||
self.is_db = False
|
||||
u = units[s]
|
||||
self.mult = u["mult"]
|
||||
self.unit = u["scalar"]
|
||||
self.type = u["type"]
|
||||
else:
|
||||
raise ValueError(f"Invalid unit: {self.raw}")
|
||||
|
||||
def __str__(self):
|
||||
return self.unit
|
||||
|
||||
|
||||
class UnitType(Enum):
|
||||
voltage = 1
|
||||
power = 2
|
||||
antenna = 3
|
||||
|
||||
|
||||
class DbConvCog(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
|
||||
@commands.command(name="dbconv", aliases=["dbc"], category=cmn.cat.ref)
|
||||
async def _db_conv(self, ctx: commands.Context,
|
||||
value: Optional[float] = None,
|
||||
unit_from: Optional[UnitConverter] = None,
|
||||
unit_to: Optional[UnitConverter] = None):
|
||||
"""
|
||||
Convert between decibels and scalar values for voltage, power, and antenna gain.
|
||||
|
||||
**Valid Units**
|
||||
*Voltage:* V, mV, µV, uV, dBV, dBmV, dBµV, dBuV
|
||||
*Power:* fW, mW, W, kW, dBf, dBm, dBW, dBk
|
||||
*Antenna Gain:* dBi, dBd, dBq
|
||||
"""
|
||||
embed = cmn.embed_factory(ctx)
|
||||
if value is not None and unit_from is not None and unit_to is not None:
|
||||
converted = convert(value, unit_from, unit_to)
|
||||
|
||||
embed.title = f"{value:.3g} {unit_from} = {converted:.3g} {unit_to}"
|
||||
embed.colour = cmn.colours.good
|
||||
else:
|
||||
embed.title = "Decibel Quick Reference"
|
||||
embed.description = (
|
||||
"Decibels are a great way to easily represent large quantities that are common in electronics. "
|
||||
"There are a few main types that are used often in radio: voltage, power, and antenna gain. "
|
||||
"Here are some commonly-used reference levels for each type:"
|
||||
)
|
||||
v_db_info = ("**dBV** = relative to 1 V\n"
|
||||
"**dBmV** = relative to 1 mV (1e-3 V)\n"
|
||||
"**dBµV** = relative to 1 µV (1e-6 V)")
|
||||
embed.add_field(name="Voltage Decibels", value=v_db_info, inline=False)
|
||||
p_db_info = ("**dBW** = relative to 1 W\n"
|
||||
"**dBk** = relative to 1 kW (1e3 W)\n"
|
||||
"**dBm** = relative to 1 mW (1e-3 W)\n"
|
||||
"**dBf** = relative to 1 fW (1e-15 W)")
|
||||
embed.add_field(name="Power Decibels", value=p_db_info, inline=False)
|
||||
a_db_info = ("**dBi** = relative to a theoretical __i__sotropic radiator in free space "
|
||||
"(equal radiation in all directions)\n"
|
||||
"**dBd** = relative to a dipole in free space (0 dBd = 2.15 dBi)\n"
|
||||
"**dBq** = relative to a quarter-wave antenna in free space (0 dBq = -0.85 dBi)")
|
||||
embed.add_field(name="Antenna Gain Decibels", value=a_db_info, inline=False)
|
||||
embed.add_field(name="Use the bot to do the conversions",
|
||||
value=f"`{opt.display_prefix}dbconv [value] [unit_from] [unit_to]`",
|
||||
inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
def setup(bot: commands.Bot):
|
||||
bot.add_cog(DbConvCog(bot))
|
||||
|
||||
|
||||
def convert(initial: float, unit1: Unit, unit2: Unit):
|
||||
if unit1.type == unit2.type:
|
||||
# dB to dB
|
||||
if unit1.is_db and unit2.is_db:
|
||||
if unit1.mult == unit2.mult:
|
||||
return initial
|
||||
elif unit1.type == UnitType.voltage:
|
||||
return _calc_volt_db(_calc_volt(initial, unit1.mult), unit2.mult)
|
||||
elif unit1.type == UnitType.power:
|
||||
return _calc_power_db(_calc_power(initial, unit1.mult), unit2.mult)
|
||||
elif unit1.type == UnitType.antenna:
|
||||
return initial + (unit1.mult - unit2.mult)
|
||||
# V/W to V/W
|
||||
elif not unit1.is_db and not unit2.is_db:
|
||||
if unit1.mult == unit2.mult:
|
||||
return initial
|
||||
return initial * unit1.mult / unit2.mult
|
||||
# dB to V/W
|
||||
elif unit1.is_db and not unit2.is_db:
|
||||
if unit1.type == UnitType.voltage:
|
||||
return _calc_volt(initial, unit1.mult) / unit2.mult
|
||||
elif unit1.type == UnitType.power:
|
||||
return _calc_power(initial, unit1.mult) / unit2.mult
|
||||
# V/W to dB
|
||||
elif not unit1.is_db and unit2.is_db:
|
||||
if unit1.type == UnitType.voltage:
|
||||
return _calc_volt_db(initial * unit1.mult, unit2.mult)
|
||||
elif unit1.type == UnitType.power:
|
||||
return _calc_power_db(initial * unit1.mult, unit2.mult)
|
||||
raise ValueError(f"Can't convert between {unit1} and {unit2}")
|
||||
|
||||
|
||||
units = {
|
||||
# voltage
|
||||
"uv": {"mult": 1e-6, "scalar": "µV", "log": "dBµV", "type": UnitType.voltage},
|
||||
"µv": {"mult": 1e-6, "scalar": "µV", "log": "dBµV", "type": UnitType.voltage},
|
||||
"mv": {"mult": 1e-3, "scalar": "mV", "log": "dBmV", "type": UnitType.voltage},
|
||||
"v": {"mult": 1, "scalar": "V", "log": "dBV", "type": UnitType.voltage},
|
||||
# power
|
||||
"fw": {"mult": 1e-15, "scalar": "fW", "log": "dBf", "type": UnitType.power},
|
||||
"f": {"mult": 1e-15, "scalar": "fW", "log": "dBf", "type": UnitType.power},
|
||||
"mw": {"mult": 1e-3, "scalar": "mW", "log": "dBm", "type": UnitType.power},
|
||||
"m": {"mult": 1e-3, "scalar": "mW", "log": "dBm", "type": UnitType.power},
|
||||
"w": {"mult": 1, "scalar": "W", "log": "dBW", "type": UnitType.power},
|
||||
"kw": {"mult": 1e3, "scalar": "kW", "log": "dBk", "type": UnitType.power},
|
||||
"k": {"mult": 1e3, "scalar": "kW", "log": "dBk", "type": UnitType.power},
|
||||
# antenna
|
||||
"q": {"mult": -0.85, "scalar": None, "log": "dBq", "type": UnitType.antenna},
|
||||
"i": {"mult": 0, "scalar": None, "log": "dBi", "type": UnitType.antenna},
|
||||
"d": {"mult": 2.15, "scalar": None, "log": "dBd", "type": UnitType.antenna},
|
||||
}
|
||||
|
||||
|
||||
def _calc_power_db(p: float, ref: float):
|
||||
return 10 * math.log10(p / ref)
|
||||
|
||||
|
||||
def _calc_power(db: float, ref: float):
|
||||
return 10 ** (db / 10) * ref
|
||||
|
||||
|
||||
def _calc_volt_db(v: float, ref: float):
|
||||
return 20 * math.log10(v / ref)
|
||||
|
||||
|
||||
def _calc_volt(db: float, ref: float):
|
||||
return 10 ** (db / 20) * ref
|
||||
|
||||
|
||||
# testing code
|
||||
if __name__ == "__main__":
|
||||
while(True):
|
||||
try:
|
||||
ip = input("> ").split()
|
||||
initial = float(ip[0])
|
||||
unit1 = Unit(ip[1])
|
||||
unit2 = Unit(ip[2])
|
||||
conv = convert(initial, unit1, unit2)
|
||||
print(f"{initial:.2f} {unit1} = {conv:.2f} {unit2}")
|
||||
except ValueError as e:
|
||||
print(e)
|
||||
@@ -10,6 +10,7 @@ the GNU General Public License, version 2.
|
||||
|
||||
import random
|
||||
|
||||
import discord
|
||||
import discord.ext.commands as commands
|
||||
|
||||
import common as cmn
|
||||
@@ -31,6 +32,21 @@ class FunCog(commands.Cog):
|
||||
"""Returns xkcd: tar."""
|
||||
await ctx.send("http://xkcd.com/1168")
|
||||
|
||||
@commands.command(name="standards", category=cmn.cat.fun)
|
||||
async def _standards(self, ctx: commands.Context):
|
||||
"""Returns xkcd: Standards."""
|
||||
await ctx.send("http://xkcd.com/927")
|
||||
|
||||
@commands.command(name="worksplit", aliases=["split", "ft8"], category=cmn.cat.fun)
|
||||
async def _worksplit(self, ctx: commands.Context):
|
||||
"""Posts "Work split you lids"."""
|
||||
fn = "worksplit.jpg"
|
||||
embed = cmn.embed_factory(ctx)
|
||||
embed.title = "Work Split, You Lids!"
|
||||
embed.set_image(url="attachment://" + fn)
|
||||
img = discord.File(cmn.paths.img / fn, filename=fn)
|
||||
await ctx.send(embed=embed, file=img)
|
||||
|
||||
@commands.command(name="xd", hidden=True, category=cmn.cat.fun)
|
||||
async def _xd(self, ctx: commands.Context):
|
||||
"""ecks dee"""
|
||||
|
||||
@@ -21,6 +21,7 @@ from resources import qcodes
|
||||
class HamCog(commands.Cog):
|
||||
def __init__(self, bot: commands.Bot):
|
||||
self.bot = bot
|
||||
self.pfxs = callsign_info.options
|
||||
|
||||
@commands.command(name="qcode", aliases=["q"], category=cmn.cat.ref)
|
||||
async def _qcode_lookup(self, ctx: commands.Context, qcode: str):
|
||||
@@ -64,22 +65,26 @@ class HamCog(commands.Cog):
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"], category=cmn.cat.ref)
|
||||
async def _vanity_prefixes(self, ctx: commands.Context, country: str = None):
|
||||
async def _vanity_prefixes(self, ctx: commands.Context, country: str = ""):
|
||||
"""Lists valid callsign prefixes for different countries."""
|
||||
if country is None:
|
||||
await ctx.send_help(ctx.command)
|
||||
return
|
||||
country = country.lower()
|
||||
embed = cmn.embed_factory(ctx)
|
||||
if country.lower() not in callsign_info.options:
|
||||
embed.title = f"{country} not found!"
|
||||
embed.description = f"Valid countries: {', '.join(callsign_info.options.keys())}"
|
||||
if country not in self.pfxs:
|
||||
desc = "Possible arguments are:\n"
|
||||
for key, val in self.pfxs.items():
|
||||
desc += f"`{key}`: {val.title}{(' ' + val.emoji if val.emoji else '')}\n"
|
||||
embed.title = f"{country} Not Found!"
|
||||
embed.description = desc
|
||||
embed.colour = cmn.colours.bad
|
||||
await ctx.send(embed=embed)
|
||||
return
|
||||
else:
|
||||
embed.title = callsign_info.options[country.lower()][0]
|
||||
embed.description = callsign_info.options[country.lower()][1]
|
||||
data = self.pfxs[country]
|
||||
embed.title = data.title + (" " + data.emoji if data.emoji else "")
|
||||
embed.description = data.desc
|
||||
embed.colour = cmn.colours.good
|
||||
|
||||
for name, val in callsign_info.options[country.lower()][2].items():
|
||||
for name, val in data.calls.items():
|
||||
embed.add_field(name=name, value=val, inline=False)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@@ -92,6 +97,26 @@ class HamCog(commands.Cog):
|
||||
embed.colour = cmn.colours.good
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="phoneticweight", aliases=["pw"], category=cmn.cat.ref)
|
||||
async def _weight(self, ctx: commands.Context, *, msg: str):
|
||||
"""Calculates the phonetic weight of a callsign or message."""
|
||||
embed = cmn.embed_factory(ctx)
|
||||
msg = msg.upper()
|
||||
weight = 0
|
||||
for char in msg:
|
||||
try:
|
||||
weight += phonetics.pweights[char]
|
||||
except KeyError:
|
||||
embed.title = "Error in calculation of phonetic weight"
|
||||
embed.description = f"Unknown character `{char}` in message"
|
||||
embed.colour = cmn.colours.bad
|
||||
await ctx.send(embed=embed)
|
||||
return
|
||||
embed.title = f"Phonetic Weight of {msg}"
|
||||
embed.description = f"The phonetic weight is **{weight}**"
|
||||
embed.colour = cmn.colours.good
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
def setup(bot: commands.Bot):
|
||||
bot.add_cog(HamCog(bot))
|
||||
|
||||
@@ -8,7 +8,6 @@ the GNU General Public License, version 2.
|
||||
"""
|
||||
|
||||
|
||||
from collections import OrderedDict
|
||||
from io import BytesIO
|
||||
|
||||
import aiohttp
|
||||
@@ -159,27 +158,24 @@ def qrz_process_info(data: dict):
|
||||
else:
|
||||
lotw = "Unknown"
|
||||
|
||||
return OrderedDict([("Name", name),
|
||||
("Country", data.get("country", None)),
|
||||
("Address", address),
|
||||
("Grid Square", data.get("grid", None)),
|
||||
("County", data.get("county", None)),
|
||||
("CQ Zone", data.get("cqzone", None)),
|
||||
("ITU Zone", data.get("ituzone", None)),
|
||||
("IOTA Designator", data.get("iota", None)),
|
||||
("Expires", data.get("expdate", None)),
|
||||
("Aliases", data.get("aliases", None)),
|
||||
("Previous Callsign", data.get("p_call", None)),
|
||||
("License Class", data.get("class", None)),
|
||||
("Trustee", data.get("trustee", None)),
|
||||
("eQSL?", eqsl),
|
||||
("Paper QSL?", mqsl),
|
||||
("LotW?", lotw),
|
||||
("QSL Info", data.get("qslmgr", None)),
|
||||
("CQ Zone", data.get("cqzone", None)),
|
||||
("ITU Zone", data.get("ituzone", None)),
|
||||
("IOTA Designator", data.get("iota", None)),
|
||||
("Born", data.get("born", None))])
|
||||
return {"Name": name,
|
||||
"Country": data.get("country", None),
|
||||
"Address": address,
|
||||
"Grid Square": data.get("grid", None),
|
||||
"County": data.get("county", None),
|
||||
"CQ Zone": data.get("cqzone", None),
|
||||
"ITU Zone": data.get("ituzone", None),
|
||||
"IOTA Designator": data.get("iota", None),
|
||||
"Expires": data.get("expdate", None),
|
||||
"Aliases": data.get("aliases", None),
|
||||
"Previous Callsign": data.get("p_call", None),
|
||||
"License Class": data.get("class", None),
|
||||
"Trustee": data.get("trustee", None),
|
||||
"eQSL?": eqsl,
|
||||
"Paper QSL?": mqsl,
|
||||
"LotW?": lotw,
|
||||
"QSL Info": data.get("qslmgr", None),
|
||||
"Born": data.get("born", None)}
|
||||
|
||||
|
||||
def setup(bot):
|
||||
|
||||
@@ -40,7 +40,7 @@ class WeatherCog(commands.Cog):
|
||||
embed.set_image(url="attachment://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"], case_insensitive=True, category=cmn.cat.weather)
|
||||
async def _weather_conditions(self, ctx: commands.Context):
|
||||
"""Gets local weather conditions from [wttr.in](http://wttr.in/).
|
||||
|
||||
|
||||
@@ -12,5 +12,5 @@ authors = ("@ClassAbbyAmplifier#2229", "@0x5c#0639")
|
||||
description = """A bot with various useful ham radio-related functions, written in Python."""
|
||||
license = "Released under the GNU General Public License v2"
|
||||
contributing = "Check out the source on GitHub, contributions welcome: https://github.com/miaowware/qrm2"
|
||||
release = "2.2.2"
|
||||
release = "2.4.0"
|
||||
bot_server = "https://discord.gg/Ntbg3J4"
|
||||
|
||||
@@ -45,8 +45,8 @@ loop = asyncio.get_event_loop()
|
||||
connector = loop.run_until_complete(conn.new_connector())
|
||||
|
||||
bot = commands.Bot(command_prefix=opt.prefix,
|
||||
description=info.description,
|
||||
help_command=commands.MinimalHelpCommand(),
|
||||
case_insensitive=True,
|
||||
description=info.description, help_command=commands.MinimalHelpCommand(),
|
||||
loop=loop,
|
||||
connector=connector)
|
||||
|
||||
@@ -82,7 +82,7 @@ async def _shutdown_bot(ctx: commands.Context):
|
||||
await bot.logout()
|
||||
|
||||
|
||||
@bot.group(name="extctl", aliases=["ex"], category=cmn.cat.admin)
|
||||
@bot.group(name="extctl", aliases=["ex"], case_insensitive=True, category=cmn.cat.admin)
|
||||
@commands.check(cmn.check_if_owner)
|
||||
async def _extctl(ctx: commands.Context):
|
||||
"""Extension control commands.
|
||||
@@ -193,7 +193,10 @@ async def _ensure_activity_time():
|
||||
try:
|
||||
tz = pytz.timezone(opt.status_tz)
|
||||
except pytz.exceptions.UnknownTimeZoneError:
|
||||
await bot.change_presence(activity=discord.Game(name="with invalid timezones."))
|
||||
status = "with invalid timezones"
|
||||
if opt.show_help:
|
||||
status += f" | {opt.display_prefix}help"
|
||||
await bot.change_presence(activity=discord.Game(name=status))
|
||||
return
|
||||
|
||||
now = datetime.now(tz=tz).time()
|
||||
@@ -203,6 +206,8 @@ async def _ensure_activity_time():
|
||||
end_time = time(hour=sts[2][0], minute=sts[2][1], tzinfo=tz)
|
||||
if start_time < now <= end_time:
|
||||
status = sts[0]
|
||||
if opt.show_help:
|
||||
status += f" | {opt.display_prefix}help"
|
||||
|
||||
await bot.change_presence(activity=discord.Game(name=status))
|
||||
|
||||
@@ -210,6 +215,8 @@ async def _ensure_activity_time():
|
||||
@tasks.loop(minutes=5)
|
||||
async def _ensure_activity_random():
|
||||
status = random.choice(opt.statuses)
|
||||
if opt.show_help:
|
||||
status += f" | {opt.display_prefix}help"
|
||||
|
||||
await bot.change_presence(activity=discord.Game(name=status))
|
||||
|
||||
@@ -217,6 +224,8 @@ async def _ensure_activity_random():
|
||||
@tasks.loop(minutes=5)
|
||||
async def _ensure_activity_fixed():
|
||||
status = opt.statuses[0]
|
||||
if opt.show_help:
|
||||
status += f" | {opt.display_prefix}help"
|
||||
|
||||
await bot.change_presence(activity=discord.Game(name=status))
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
discord.py
|
||||
ctyparser
|
||||
beautifulsoup4
|
||||
lxml
|
||||
pytz
|
||||
discord.py==1.3.4
|
||||
ctyparser==2.0.0.post1
|
||||
beautifulsoup4==4.9.1
|
||||
lxml==4.5.2
|
||||
pytz==2020.1
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Information about callsigns for the vanity prefixes command in hamcog.
|
||||
Information about callsigns for the prefixes command in hamcog.
|
||||
---
|
||||
Copyright (C) 2019-2020 Abigail Gold, 0x5c
|
||||
|
||||
@@ -7,50 +7,12 @@ This file is part of discord-qrmbot and is released under the terms of
|
||||
the GNU General Public License, version 2.
|
||||
"""
|
||||
|
||||
from .callsigninfos import (us, ca)
|
||||
from common import CallsignInfoData
|
||||
|
||||
from collections import OrderedDict
|
||||
|
||||
|
||||
us_calls_title = "Valid US Vanity Callsigns"
|
||||
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.")
|
||||
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 (2x2)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** AL, KL, NL, WL (2x1)\n"
|
||||
"**Caribbean:** KP, NP, WP (2x1)\n"
|
||||
"**Pacific:** AH, KH, NH, WH (2x1)")),
|
||||
("**Group B** (Advanced and Extra Only)", ("**Any:** KA-KZ, NA-NZ, WA-WZ (2x2)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** AL (2x2)\n"
|
||||
"**Caribbean:** KP (2x2)\n"
|
||||
"**Pacific:** AH (2x2)")),
|
||||
("**Group C** (Technician, General, Advanced, Extra Only)", ("**Any Region:** K, N, W (1x3)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** KL, NL, WL (2x2)\n"
|
||||
"**Caribbean:** NP, WP (2x2)\n"
|
||||
"**Pacific:** KH, NH, WH (2x2)")),
|
||||
("**Group D** (Any License Class)", ("**Any Region:** KA-KZ, WA-WZ (2x3)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** KL, WL (2x3)\n"
|
||||
"**Caribbean:** KP, WP (2x3)\n"
|
||||
"**Pacific:** KH, WH (2x3)")),
|
||||
("**Unavailable**", ("- KA2AA-KA9ZZ: US Army in Japan\n"
|
||||
"- KC4AAA-KC4AAF: NSF in Antartica\n"
|
||||
"- KC4USA-KC4USZ: US Navy in Antartica\n"
|
||||
"- KG4AA-KG4ZZ: US Navy in Guantanamo Bay\n"
|
||||
"- KL9KAA-KL9KHZ: US military in Korea\n"
|
||||
"- KC6AA-KC6ZZ: Former US (Eastern and Western Caroline Islands), "
|
||||
"now Federated States of Micronesia (V6) and Republic of Palau (T8)\n"
|
||||
"- KX6AA-KX6ZZ: Former US (Marshall Islands), "
|
||||
"now Republic of the Marshall Islands (V73)\n"
|
||||
"- Any suffix SOS or QRA-QUZ\n"
|
||||
"- Any 2x3 with X as the first suffix letter\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: \"Group X\"\n"
|
||||
"- Any 2x1, 2x2, or 2x3 with KP, NP, WP prefix and 0, 6, 7, 8, 9 number\n"
|
||||
"- Any 1x1 callsign: Special Event"))])
|
||||
|
||||
# format: country: (title, description, text)
|
||||
options = {"us": (us_calls_title, us_calls_desc, us_calls)}
|
||||
options = {
|
||||
"us": CallsignInfoData([us.title, us.desc, us.calls, us.emoji]),
|
||||
"ca": CallsignInfoData([ca.title, ca.desc, ca.calls, ca.emoji]),
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
"""
|
||||
Callsign info for various countries
|
||||
"""
|
||||
@@ -0,0 +1,47 @@
|
||||
"""
|
||||
Information about callsigns for the CA prefixes command in hamcog.
|
||||
---
|
||||
Copyright (C) 2019-2020 Abigail Gold, 0x5c
|
||||
|
||||
This file is part of discord-qrmbot and is released under the terms of
|
||||
the GNU General Public License, version 2.
|
||||
"""
|
||||
|
||||
|
||||
title = "Canadian Callsign Rules"
|
||||
emoji = "🇨🇦"
|
||||
desc = ("Canadian operators are limited to callsigns with the prefixes of their address' province/territory. "
|
||||
"Initially, operators can choose a callsign with a 3-letter suffix. "
|
||||
"Later on, they can apply to change or for additional callsigns. "
|
||||
"Operators can only hold one 2-letter suffix callsign, but many 3-letter suffix callsigns. "
|
||||
"If the number of 2-letter suffix callsigns exceeds 80% of the total available, "
|
||||
"operators can only choose a 2-letter suffix after holding a license for 5 years. "
|
||||
"If the operator is a family member of a deceased operator, they are not bound by this restriction. "
|
||||
"Data from [ISED Canada (RIC-9)](https://www.ic.gc.ca/eic/site/smt-gst.nsf/eng/sf02102.html).")
|
||||
calls = {
|
||||
"Provinces": (
|
||||
"**Nova Scotia:** VE1 and VA1\n"
|
||||
"**Québec:** VE2 and VA2\n"
|
||||
"**Ontario:** VE3 and VA3\n"
|
||||
"**Manitoba:** VE4 and VA4\n"
|
||||
"**Saskatchewan:** VE5 and VA5\n"
|
||||
"**Alberta:** VE6 and VA6\n"
|
||||
"**British Columbia:** VE7 and VA7\n"
|
||||
"**New Brunswick:** VE9\n"
|
||||
"**Newfoundland:** VO1\n"
|
||||
"**Labrador:** VO2\n"
|
||||
"**Prince Edward Island:** VY2\n"
|
||||
),
|
||||
"Territories": (
|
||||
"**Northwest Territories:** VE8\n"
|
||||
"**Nunavut:** VY0\n"
|
||||
"**Yukon:** VY1\n"
|
||||
),
|
||||
"Other": (
|
||||
"**International Waters:** VE0\n"
|
||||
"**Government of Canada:** VY9\n"
|
||||
"**Sable Island:** CY0\n"
|
||||
"**St-Paul Island:** CY9\n"
|
||||
),
|
||||
"Special Event": "Various prefixes in the ranges: CF-CK, CY-CZ, VA-VG, VO, VX-VY, XJ-XO"
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
Information about callsigns for the US prefixes command in hamcog.
|
||||
---
|
||||
Copyright (C) 2019-2020 Abigail Gold, 0x5c
|
||||
|
||||
This file is part of discord-qrmbot and is released under the terms of
|
||||
the GNU General Public License, version 2.
|
||||
"""
|
||||
|
||||
|
||||
title = "US Callsign Rules"
|
||||
emoji = "🇺🇸"
|
||||
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.")
|
||||
calls = {
|
||||
"**Group A** (Extra Only)": ("**Any:** K, N, W (1x2)\n"
|
||||
" AA-AL, KA-KZ, NA-NZ, WA-WZ (2x1)\n"
|
||||
" AA-AL (2x2)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** AL, KL, NL, WL (2x1)\n"
|
||||
"**Caribbean:** KP, NP, WP (2x1)\n"
|
||||
"**Pacific:** AH, KH, NH, WH (2x1)"),
|
||||
"**Group B** (Advanced and Extra Only)": ("**Any:** KA-KZ, NA-NZ, WA-WZ (2x2)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** AL (2x2)\n"
|
||||
"**Caribbean:** KP (2x2)\n"
|
||||
"**Pacific:** AH (2x2)"),
|
||||
"**Group C** (Technician, General, Advanced, Extra Only)": ("**Any Region:** K, N, W (1x3)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** KL, NL, WL (2x2)\n"
|
||||
"**Caribbean:** NP, WP (2x2)\n"
|
||||
"**Pacific:** KH, NH, WH (2x2)"),
|
||||
"**Group D** (Any License Class)": ("**Any Region:** KA-KZ, WA-WZ (2x3)\n"
|
||||
"*Except*\n"
|
||||
"**Alaska:** KL, WL (2x3)\n"
|
||||
"**Caribbean:** KP, WP (2x3)\n"
|
||||
"**Pacific:** KH, WH (2x3)"),
|
||||
"**Unavailable**": ("- KA2AA-KA9ZZ: US Army in Japan\n"
|
||||
"- KC4AAA-KC4AAF: NSF in Antartica\n"
|
||||
"- KC4USA-KC4USZ: US Navy in Antartica\n"
|
||||
"- KG4AA-KG4ZZ: US Navy in Guantanamo Bay\n"
|
||||
"- KL9KAA-KL9KHZ: US military in Korea\n"
|
||||
"- KC6AA-KC6ZZ: Former US (Eastern and Western Caroline Islands), "
|
||||
"now Federated States of Micronesia (V6) and Republic of Palau (T8)\n"
|
||||
"- KX6AA-KX6ZZ: Former US (Marshall Islands), "
|
||||
"now Republic of the Marshall Islands (V73)\n"
|
||||
"- Any suffix SOS or QRA-QUZ\n"
|
||||
"- Any 2x3 with X as the first suffix letter\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: \"Group X\"\n"
|
||||
"- Any 2x1, 2x2, or 2x3 with KP, NP, WP prefix and 0, 6, 7, 8, 9 number\n"
|
||||
"- Any 1x1 callsign: Special Event")
|
||||
}
|
||||
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 102 KiB |
|
After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 73 KiB |
|
After Width: | Height: | Size: 27 KiB |
@@ -1,6 +1,10 @@
|
||||
{
|
||||
"arrl": ["arrl-rac.png", "ARRL Sections", "ARRL Sections", "", "", "🇺🇸"],
|
||||
"rac": ["arrl-rac.png", "RAC Sections", "RAC Sections", "", "", "🇨🇦"],
|
||||
"arrl": ["arrl-rac.png", "ARRL Sections", "ARRL Sections", "", "[EI8IC](https://www.mapability.com/ei8ic/maps/maps.php)", "🇺🇸"],
|
||||
"rac": ["arrl-rac.png", "RAC Sections", "RAC Sections", "", "[EI8IC](https://www.mapability.com/ei8ic/maps/maps.php)", "🇨🇦"],
|
||||
"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)", "🇺🇸"]
|
||||
"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)", "🇺🇸"],
|
||||
"ca": ["ca.png", "Canada's Prefixes", "Map of the prefix regions in Canada", "", "[Denelson83 (Wikimedia Commons)](https://commons.wikimedia.org/wiki/File:Amateur_radio_prefixes_in_Canada.svg)", "🇨🇦"],
|
||||
"ituz": ["itu-zones.png", "ITU Zones", "ITU Zones", "", "[EI8IC](https://www.mapability.com/ei8ic/maps/maps.php)", "🇺🇳"],
|
||||
"itur": ["itu-regions.png", "ITU Regions", "ITU Regions", "These are also used by the IARU for their regions.", "[EI8IC](https://www.mapability.com/ei8ic/maps/maps.php)", "🇺🇳"],
|
||||
"cq": ["cq-zones.png", "CQ Zones", "CQ Zones", "These are used for the CQWW contest.", "[EI8IC](https://www.mapability.com/ei8ic/maps/maps.php)", "🌐"]
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 304 KiB |
@@ -36,3 +36,43 @@ phonetics = {
|
||||
"y": "yankee",
|
||||
"z": "zulu"
|
||||
}
|
||||
|
||||
pweights = {
|
||||
"A": 2,
|
||||
"B": 2,
|
||||
"C": 2,
|
||||
"D": 2,
|
||||
"E": 2,
|
||||
"F": 2,
|
||||
"G": 1,
|
||||
"H": 2,
|
||||
"I": 3,
|
||||
"J": 3,
|
||||
"K": 2,
|
||||
"L": 2,
|
||||
"M": 1,
|
||||
"N": 3,
|
||||
"O": 2,
|
||||
"P": 2,
|
||||
"Q": 2,
|
||||
"R": 3,
|
||||
"S": 3,
|
||||
"T": 2,
|
||||
"U": 3,
|
||||
"V": 2,
|
||||
"W": 2,
|
||||
"X": 2,
|
||||
"Y": 2,
|
||||
"Z": 2,
|
||||
"0": 2,
|
||||
"1": 1,
|
||||
"2": 1,
|
||||
"3": 1,
|
||||
"4": 1,
|
||||
"5": 1,
|
||||
"6": 1,
|
||||
"7": 2,
|
||||
"8": 1,
|
||||
"9": 2,
|
||||
"/": 1,
|
||||
}
|
||||
|
||||
@@ -15,7 +15,10 @@ Settings and options for the bot.
|
||||
|
||||
# The prefix for the bot (str). Define a list of stings for multiple prefixes.
|
||||
# ie: `["?", "!", "pls "]`
|
||||
prefix = "?"
|
||||
prefix = ["? ", "?"]
|
||||
|
||||
# The prefix to use for display purposes (ex: status message).
|
||||
display_prefix = "?"
|
||||
|
||||
# Whether the bot should print full stacktraces for normal exceptions: `True`,
|
||||
# or be nice and only print small messages: `False` (the default).
|
||||
@@ -27,7 +30,7 @@ debug = False
|
||||
owners_uids = (200102491231092736,)
|
||||
|
||||
# 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", "dbconv"]
|
||||
|
||||
# Either "time", "random", or "fixed" (first item in statuses)
|
||||
status_mode = "fixed"
|
||||
@@ -46,6 +49,9 @@ time_statuses = [("with lids on 3.840", (00, 00), (6, 00)),
|
||||
("with lids on 7.200", (18, 00), (20, 00)),
|
||||
("with lids on 3.840", (20, 00), (23, 59))]
|
||||
|
||||
# append " | {display_prefix}help" to the Discord playing status
|
||||
show_help = False
|
||||
|
||||
# Emoji IDs and keywords for emoji reactions
|
||||
# Use the format {emoji_id (int): ("tuple", "of", "lowercase", "keywords")}
|
||||
msg_reacts = {}
|
||||
|
||||