mirror of
https://github.com/miaowware/qrm2.git
synced 2026-06-03 06:24:52 -04:00
Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fed97a03b3 | |||
| 5f796d479e | |||
| 3ba55d4c35 | |||
| f4ed93dc76 | |||
| 2cb4b03532 | |||
| bc93462c29 | |||
| 6867c45c8c | |||
| dcbb7acab8 | |||
| 3803ce6045 | |||
| 8ca4911072 | |||
| 6e2468f04f | |||
| b17a8a1749 | |||
| 8dfa7001ef | |||
| f6ed8430b9 | |||
| d650cbd6c1 | |||
| 4d9f9d1b19 | |||
| 1c649aacc2 | |||
| 2049ca9fca |
@@ -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
|
||||||
+30
-1
@@ -7,6 +7,30 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
|
||||||
|
## [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.
|
||||||
|
- Added/fixed/removed typing indicators in numerous commands.
|
||||||
|
|
||||||
|
|
||||||
## [2.2.1] - 2020-02-20
|
## [2.2.1] - 2020-02-20
|
||||||
### Fixed
|
### Fixed
|
||||||
- Fixed issue where some HamStudy pools will become unselectable.
|
- Fixed issue where some HamStudy pools will become unselectable.
|
||||||
@@ -81,7 +105,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
## 1.0.0 - 2019-07-31 [YANKED]
|
## 1.0.0 - 2019-07-31 [YANKED]
|
||||||
|
|
||||||
|
|
||||||
[Unreleased]: https://github.com/miaowware/qrm2/compare/v2.2.0...HEAD
|
[Unreleased]: https://github.com/miaowware/qrm2/compare/v2.3.1...HEAD
|
||||||
|
[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
|
[2.2.0]: https://github.com/miaowware/qrm2/releases/tag/v2.2.0
|
||||||
[2.1.0]: https://github.com/miaowware/qrm2/releases/tag/v2.1.0
|
[2.1.0]: https://github.com/miaowware/qrm2/releases/tag/v2.1.0
|
||||||
[2.0.0]: https://github.com/miaowware/qrm2/releases/tag/v2.0.0
|
[2.0.0]: https://github.com/miaowware/qrm2/releases/tag/v2.0.0
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ See [README-DOCKER.md](./README-DOCKER.md)
|
|||||||
|
|
||||||
### Without Docker
|
### 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).
|
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).
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"_id": "56956f51f65e5c590272e372",
|
||||||
|
"appears": "2016-04-01T06:00:00.000Z",
|
||||||
|
"class": "Amateur Extra",
|
||||||
|
"subtext": "Expires Jul 1, 2020",
|
||||||
|
"valid_from": "2016-07-01T06:00:00.000Z",
|
||||||
|
"expires": "2020-07-01T06:00:00.000Z",
|
||||||
|
"official_name": "Element 4",
|
||||||
|
"id": "E4_2016",
|
||||||
|
"slug": "extra2016",
|
||||||
|
"passing": 37,
|
||||||
|
"year": 2016,
|
||||||
|
"pool": [{
|
||||||
|
"_id": "5cd63f15910d9b003d545bd7",
|
||||||
|
"qcount": 4,
|
||||||
|
"id": "E5",
|
||||||
|
"name": "ELECTRICAL PRINCIPLES",
|
||||||
|
"sections": [{
|
||||||
|
"_id": "5cd63f15910d9b003d545beb",
|
||||||
|
"id": "E5C",
|
||||||
|
"questions": [{
|
||||||
|
"_id": "5cd63f15910d9b003d545bef",
|
||||||
|
"keywords": ["4"],
|
||||||
|
"answer": "B",
|
||||||
|
"answers": {
|
||||||
|
"A": "Point 2",
|
||||||
|
"B": "Point 4",
|
||||||
|
"C": "Point 5",
|
||||||
|
"D": "Point 6"
|
||||||
|
},
|
||||||
|
"fccpart": "",
|
||||||
|
"id": "E5C14",
|
||||||
|
"image": "E5-2.png",
|
||||||
|
"text": "Which point on Figure E5-2 best represents the impedance of a series circuit consisting of a 400 ohm resistor and a 38 picofarad capacitor at 14 MHz?"
|
||||||
|
}],
|
||||||
|
"summary": "Coordinate systems and phasors in electronics: Rectangular Coordinates; Polar Coordinates; Phasors"
|
||||||
|
}]
|
||||||
|
}],
|
||||||
|
"updated": "2019-05-11T03:18:46.121Z",
|
||||||
|
"category": "default",
|
||||||
|
"testIdEnd": 19999,
|
||||||
|
"testIdStart": 10000,
|
||||||
|
"__v": 12,
|
||||||
|
"mat_icon": "flash_on",
|
||||||
|
"tagline": "Serious General operators only! This is the most advanced US license class!",
|
||||||
|
"keywords": ["ham radio extra test prep", "amateur extra class radio study", "amateur extra class ham exam", "ham radio amateur extra test 2016", "2016 amateur extra class", "ham radio license exam", "extra class flash card"],
|
||||||
|
"replaces": "E4_2012"
|
||||||
|
}
|
||||||
+344
-283
@@ -16,8 +16,10 @@ the GNU General Public License, version 2.
|
|||||||
# KC4USA: reserved, no call history, *but* has application history
|
# KC4USA: reserved, no call history, *but* has application history
|
||||||
|
|
||||||
|
|
||||||
|
import re
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
from bs4 import BeautifulSoup
|
import ae7qparser
|
||||||
|
|
||||||
import discord.ext.commands as commands
|
import discord.ext.commands as commands
|
||||||
|
|
||||||
@@ -29,7 +31,7 @@ class AE7QCog(commands.Cog):
|
|||||||
self.bot = bot
|
self.bot = bot
|
||||||
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
|
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):
|
async def _ae7q_lookup(self, ctx: commands.Context):
|
||||||
"""Looks up a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/)."""
|
"""Looks up a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/)."""
|
||||||
if ctx.invoked_subcommand is None:
|
if ctx.invoked_subcommand is None:
|
||||||
@@ -40,64 +42,86 @@ class AE7QCog(commands.Cog):
|
|||||||
"""Looks up the history of a callsign on [ae7q.com](http://ae7q.com/)."""
|
"""Looks up the history of a callsign on [ae7q.com](http://ae7q.com/)."""
|
||||||
with ctx.typing():
|
with ctx.typing():
|
||||||
callsign = callsign.upper()
|
callsign = callsign.upper()
|
||||||
desc = ""
|
|
||||||
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
|
|
||||||
embed = cmn.embed_factory(ctx)
|
|
||||||
|
|
||||||
async with self.session.get(base_url + callsign) as resp:
|
call_data = ae7qparser.get_call(callsign)
|
||||||
if resp.status != 200:
|
|
||||||
raise cmn.BotHTTPError(resp)
|
|
||||||
page = await resp.text()
|
|
||||||
|
|
||||||
soup = BeautifulSoup(page, features="html.parser")
|
|
||||||
tables = [[row for row in table.find_all("tr")] for table in soup.select("table.Database")]
|
|
||||||
|
|
||||||
table = tables[0]
|
|
||||||
|
|
||||||
# find the first table in the page, and use it to make a description
|
|
||||||
if len(table[0]) == 1:
|
|
||||||
for row in table:
|
|
||||||
desc += " ".join(row.getText().split())
|
|
||||||
desc += "\n"
|
|
||||||
desc = desc.replace(callsign, f"`{callsign}`")
|
|
||||||
table = tables[1]
|
|
||||||
|
|
||||||
table_headers = table[0].find_all("th")
|
|
||||||
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
|
|
||||||
|
|
||||||
# catch if the wrong table was selected
|
|
||||||
if first_header is None or first_header != "Entity Name":
|
|
||||||
embed.title = f"AE7Q History for {callsign}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + callsign
|
|
||||||
embed.description = desc
|
|
||||||
embed.description += f"\nNo records found for `{callsign}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table = await process_table(table[1:])
|
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"AE7Q History for {callsign}"
|
embed.title = f"AE7Q Callsign History for {callsign}"
|
||||||
|
embed.url = call_data.query_url
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
embed.url = base_url + callsign
|
embed.description = ""
|
||||||
|
|
||||||
# add the first three rows of the table to the embed
|
if isinstance(call_data, ae7qparser.Ae7qCallData):
|
||||||
for row in table[0:3]:
|
if call_data.conditions:
|
||||||
header = f"**{row[0]}** ({row[1]})" # **Name** (Applicant Type)
|
embed.description = " ".join([
|
||||||
body = (f"Class: *{row[2]}*\n"
|
" ".join([y.strip() for y in x]) for x in call_data.conditions
|
||||||
f"Region: *{row[3]}*\n"
|
]).replace(callsign, f"`{callsign}`")
|
||||||
f"Status: *{row[4]}*\n"
|
|
||||||
f"Granted: *{row[5]}*\n"
|
if not call_data.call_history:
|
||||||
f"Effective: *{row[6]}*\n"
|
if not call_data.event_callsign_history:
|
||||||
f"Cancelled: *{row[7]}*\n"
|
embed.colour = cmn.colours.bad
|
||||||
f"Expires: *{row[8]}*")
|
embed.description += f"\nNo records found for `{callsign}`"
|
||||||
|
else:
|
||||||
|
# add the first five rows of the event callsign history to the embed
|
||||||
|
for row in call_data.event_callsign_history[0:5]:
|
||||||
|
header = f"**{row.start_date:%Y-%m-%d}-{row.end_date:%Y-%m-%d}**"
|
||||||
|
body = (f"Requestor: *{row.entity_name} ({row.callsign})*\n"
|
||||||
|
f"Event: *{row.event_name}*")
|
||||||
embed.add_field(name=header, value=body, inline=False)
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
if len(table) > 3:
|
if len(call_data.event_callsign_history) > 5:
|
||||||
desc += f"\nRecords 1 to 3 of {len(table)}. See ae7q.com for more..."
|
embed.description += (f"\nRecords 1 to 5 of {len(call_data.event_callsign_history)}. "
|
||||||
|
f"See [ae7q.com]({call_data.query_url}) for more...")
|
||||||
|
|
||||||
embed.description = desc
|
else:
|
||||||
|
# add the first three rows of the callsign history to the embed
|
||||||
|
for row in call_data.call_history[0:3]:
|
||||||
|
header = f"**{row.entity_name}** ({row.applicant_type})"
|
||||||
|
body = (f"Class: *{row.operator_class}*\n"
|
||||||
|
f"Region: *{row.region_state}*\n"
|
||||||
|
f"Status: *{row.license_status}*\n")
|
||||||
|
if row.grant_date:
|
||||||
|
body += f"Granted: *{row.grant_date:%Y-%m-%d}*\n"
|
||||||
|
if row.effective_date:
|
||||||
|
body += f"Effective: *{row.effective_date:%Y-%m-%d}*\n"
|
||||||
|
if row.cancel_date:
|
||||||
|
body += f"Cancelled: *{row.cancel_date:%Y-%m-%d}*\n"
|
||||||
|
if row.expire_date:
|
||||||
|
body += f"Expires: *{row.expire_date:%Y-%m-%d}*\n"
|
||||||
|
|
||||||
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
|
if len(call_data.call_history) > 3:
|
||||||
|
embed.description += (f"\nRecords 1 to 3 of {len(call_data.call_history)}. "
|
||||||
|
f"See [ae7q.com]({call_data.query_url}) for more...")
|
||||||
|
|
||||||
|
elif isinstance(call_data, ae7qparser.Ae7qCanadianCallData):
|
||||||
|
if not call_data.callsign_data:
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description += f"\nNo records found for `{callsign}`"
|
||||||
|
else:
|
||||||
|
if call_data.given_names != "" and call_data.surname != "":
|
||||||
|
embed.add_field(name="Name", value=f"{call_data.given_names} {call_data.surname}", inline=True)
|
||||||
|
if call_data.address != "":
|
||||||
|
embed.add_field(name="Address", value=call_data.address, inline=True)
|
||||||
|
if call_data.locality != "":
|
||||||
|
embed.add_field(name="Locality", value=call_data.locality, inline=True)
|
||||||
|
if call_data.province != "":
|
||||||
|
embed.add_field(name="Province", value=call_data.province, inline=True)
|
||||||
|
if call_data.postal_code != "":
|
||||||
|
embed.add_field(name="Postal Code", value=call_data.postal_code, inline=True)
|
||||||
|
if call_data.country != "":
|
||||||
|
embed.add_field(name="Country", value=call_data.country, inline=True)
|
||||||
|
if call_data.region != "":
|
||||||
|
embed.add_field(name="Region", value=call_data.region, inline=True)
|
||||||
|
if call_data.grid_square != "":
|
||||||
|
embed.add_field(name="Grid Square", value=call_data.grid_square, inline=True)
|
||||||
|
if call_data.qualifications != "":
|
||||||
|
embed.add_field(name="License Qualifications", value=call_data.qualifications, inline=True)
|
||||||
|
|
||||||
|
else:
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description += f"\nNo records found for `{callsign}`"
|
||||||
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@@ -106,202 +130,283 @@ class AE7QCog(commands.Cog):
|
|||||||
"""Looks up the licenses for which a licensee is trustee on [ae7q.com](http://ae7q.com/)."""
|
"""Looks up the licenses for which a licensee is trustee on [ae7q.com](http://ae7q.com/)."""
|
||||||
with ctx.typing():
|
with ctx.typing():
|
||||||
callsign = callsign.upper()
|
callsign = callsign.upper()
|
||||||
desc = ""
|
|
||||||
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
|
|
||||||
embed = cmn.embed_factory(ctx)
|
|
||||||
|
|
||||||
async with self.session.get(base_url + callsign) as resp:
|
call_data = ae7qparser.get_call(callsign)
|
||||||
if resp.status != 200:
|
|
||||||
raise cmn.BotHTTPError(resp)
|
|
||||||
page = await resp.text()
|
|
||||||
|
|
||||||
soup = BeautifulSoup(page, features="html.parser")
|
|
||||||
tables = [[row for row in table.find_all("tr")] for table in soup.select("table.Database")]
|
|
||||||
|
|
||||||
try:
|
|
||||||
table = tables[2] if len(tables[0][0]) == 1 else tables[1]
|
|
||||||
except IndexError:
|
|
||||||
embed.title = f"AE7Q Trustee History for {callsign}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + callsign
|
|
||||||
embed.description = desc
|
|
||||||
embed.description += f"\nNo records found for `{callsign}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table_headers = table[0].find_all("th")
|
|
||||||
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
|
|
||||||
|
|
||||||
# catch if the wrong table was selected
|
|
||||||
if first_header is None or not first_header.startswith("With"):
|
|
||||||
embed.title = f"AE7Q Trustee History for {callsign}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + callsign
|
|
||||||
embed.description = desc
|
|
||||||
embed.description += f"\nNo records found for `{callsign}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table = await process_table(table[2:])
|
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"AE7Q Trustee History for {callsign}"
|
embed.title = f"AE7Q Trustee History for {callsign}"
|
||||||
|
embed.url = call_data.query_url
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
embed.url = base_url + callsign
|
embed.description = ""
|
||||||
|
|
||||||
|
if isinstance(call_data, ae7qparser.Ae7qCallData):
|
||||||
|
if not call_data.trustee_history:
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description += f"\nNo records found for `{callsign}`"
|
||||||
|
else:
|
||||||
|
# add the first three rows of the trustee history to the embed
|
||||||
|
for row in call_data.trustee_history[0:3]:
|
||||||
|
header = f"**{row.callsign}** ({row.applicant_type})"
|
||||||
|
body = (f"Name: *{row.entity_name}*\n"
|
||||||
|
f"Region: *{row.region_state}*\n"
|
||||||
|
f"Status: *{row.license_status}*\n")
|
||||||
|
if row.grant_date:
|
||||||
|
body += f"Granted: *{row.grant_date:%Y-%m-%d}*\n"
|
||||||
|
if row.effective_date:
|
||||||
|
body += f"Effective: *{row.effective_date:%Y-%m-%d}*\n"
|
||||||
|
if row.cancel_date:
|
||||||
|
body += f"Cancelled: *{row.cancel_date:%Y-%m-%d}*\n"
|
||||||
|
if row.expire_date:
|
||||||
|
body += f"Expires: *{row.expire_date:%Y-%m-%d}*\n"
|
||||||
|
|
||||||
# add the first three rows of the table to the embed
|
|
||||||
for row in table[0:3]:
|
|
||||||
header = f"**{row[0]}** ({row[3]})" # **Name** (Applicant Type)
|
|
||||||
body = (f"Name: *{row[2]}*\n"
|
|
||||||
f"Region: *{row[1]}*\n"
|
|
||||||
f"Status: *{row[4]}*\n"
|
|
||||||
f"Granted: *{row[5]}*\n"
|
|
||||||
f"Effective: *{row[6]}*\n"
|
|
||||||
f"Cancelled: *{row[7]}*\n"
|
|
||||||
f"Expires: *{row[8]}*")
|
|
||||||
embed.add_field(name=header, value=body, inline=False)
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
if len(table) > 3:
|
if len(call_data.trustee_history) > 3:
|
||||||
desc += f"\nRecords 1 to 3 of {len(table)}. See ae7q.com for more..."
|
embed.description += (f"\nRecords 1 to 3 of {len(call_data.trustee_history)}. "
|
||||||
|
f"See [ae7q.com]({call_data.query_url}) for more...")
|
||||||
|
|
||||||
embed.description = desc
|
else:
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description += f"\nNo records found for `{callsign}`"
|
||||||
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@_ae7q_lookup.command(name="applications", aliases=["a"], category=cmn.cat.lookup)
|
@_ae7q_lookup.command(name="applications", aliases=["a", "apps"], category=cmn.cat.lookup)
|
||||||
async def _ae7q_applications(self, ctx: commands.Context, callsign: str):
|
async def _ae7q_applications(self, ctx: commands.Context, query: str):
|
||||||
"""Looks up the application history for a callsign on [ae7q.com](http://ae7q.com/)."""
|
"""Looks up the application history for a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/)."""
|
||||||
"""
|
|
||||||
with ctx.typing():
|
with ctx.typing():
|
||||||
callsign = callsign.upper()
|
query = query.upper()
|
||||||
desc = ""
|
|
||||||
base_url = "http://ae7q.com/query/data/CallHistory.php?CALL="
|
|
||||||
embed = cmn.embed_factory(ctx)
|
|
||||||
|
|
||||||
async with self.session.get(base_url + callsign) as resp:
|
# LID
|
||||||
if resp.status != 200:
|
if re.match(r"L\d+", query):
|
||||||
raise cmn.BotHTTPError(resp)
|
data = ae7qparser.get_licensee_id(query)
|
||||||
page = await resp.text()
|
# FRN
|
||||||
|
elif re.match(r"\d{10}", query):
|
||||||
soup = BeautifulSoup(page, features="html.parser")
|
data = ae7qparser.get_frn(query)
|
||||||
tables = [[row for row in table.find_all("tr")] for table in soup.select("table.Database")]
|
# callsign
|
||||||
|
else:
|
||||||
table = tables[0]
|
data = ae7qparser.get_call(query)
|
||||||
|
|
||||||
# find the first table in the page, and use it to make a description
|
|
||||||
if len(table[0]) == 1:
|
|
||||||
for row in table:
|
|
||||||
desc += " ".join(row.getText().split())
|
|
||||||
desc += "\n"
|
|
||||||
desc = desc.replace(callsign, f"`{callsign}`")
|
|
||||||
|
|
||||||
# select the last table to get applications
|
|
||||||
table = tables[-1]
|
|
||||||
|
|
||||||
table_headers = table[0].find_all("th")
|
|
||||||
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
|
|
||||||
|
|
||||||
# catch if the wrong table was selected
|
|
||||||
if first_header is None or not first_header.startswith("Receipt"):
|
|
||||||
embed.title = f"AE7Q Application History for {callsign}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + callsign
|
|
||||||
embed.description = desc
|
|
||||||
embed.description += f"\nNo records found for `{callsign}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table = await process_table(table[1:])
|
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"AE7Q Application History for {callsign}"
|
embed.title = f"AE7Q Application History for {query}"
|
||||||
|
embed.url = data.query_url
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
embed.url = base_url + callsign
|
embed.description = ""
|
||||||
|
|
||||||
# add the first three rows of the table to the embed
|
if not data.application_history:
|
||||||
for row in table[0:3]:
|
embed.colour = cmn.colours.bad
|
||||||
header = f"**{row[1]}** ({row[3]})" # **Name** (Callsign)
|
embed.description += f"\nNo records found for `{query}`"
|
||||||
body = (f"Received: *{row[0]}*\n"
|
else:
|
||||||
f"Region: *{row[2]}*\n"
|
# add the first three rows of the app history to the embed
|
||||||
f"Purpose: *{row[5]}*\n"
|
if isinstance(data.application_history, ae7qparser.ApplicationsHistoryTable):
|
||||||
f"Last Action: *{row[7]}*\n"
|
for row in data.application_history[0:3]:
|
||||||
f"Application Status: *{row[8]}*\n")
|
header = f"**{row.uls_file_number[0]}** ({row.receipt_date:%Y-%m-%d})"
|
||||||
|
body = (f"Name: *{row.entity_name}*\n"
|
||||||
|
f"Callsign: *{row.application_callsign}*\n"
|
||||||
|
f"Region: *{row.region_state}*\n"
|
||||||
|
f"Purpose: *{row.application_purpose}*\n")
|
||||||
|
if row.payment_date:
|
||||||
|
body += f"Payment: *{row.payment_date:%Y-%m-%d}*\n"
|
||||||
|
if row.last_action_date:
|
||||||
|
body += f"Last Action: *{row.last_action_date:%Y-%m-%d}*\n"
|
||||||
|
body += f"Status: *{row.application_status}*"
|
||||||
embed.add_field(name=header, value=body, inline=False)
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
if len(table) > 3:
|
elif isinstance(data.application_history, ae7qparser.VanityApplicationsHistoryTable):
|
||||||
desc += f"\nRecords 1 to 3 of {len(table)}. See ae7q.com for more..."
|
for row in data.application_history[0:3]:
|
||||||
|
header = f"**{row.uls_file_number[0]}** ({row.receipt_date:%Y-%m-%d})"
|
||||||
|
body = (f"Callsign: *{row.application_callsign}*\n"
|
||||||
|
f"Region: *{row.region_state}*\n"
|
||||||
|
f"Operator Class: *{row.operator_class}*\n"
|
||||||
|
f"Purpose: *{row.application_purpose}*\n")
|
||||||
|
if row.payment_date:
|
||||||
|
body += f"Payment: *{row.payment_date:%Y-%m-%d}*\n"
|
||||||
|
if row.last_action_date:
|
||||||
|
body += f"Last Action: *{row.last_action_date:%Y-%m-%d}*\n"
|
||||||
|
body += f"Status: *{row.application_status}*\n"
|
||||||
|
if row.applied_callsigns:
|
||||||
|
body += (f"Callsign{'s' if len(row.applied_callsigns) > 1 else ''} "
|
||||||
|
f"Applied For: *{', '.join(row.applied_callsigns)}*")
|
||||||
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
embed.description = desc
|
if len(data.application_history) > 3:
|
||||||
|
embed.description += (f"\nRecords 1 to 3 of {len(data.application_history)}. "
|
||||||
|
f"See [ae7q.com]({data.query_url}) for more...")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@_ae7q_lookup.command(name="pending_apps", aliases=["pa"], category=cmn.cat.lookup)
|
||||||
|
async def _ae7q_pending_applications(self, ctx: commands.Context, query: str):
|
||||||
|
"""Looks up the pending applications for a callsign, FRN, or Licensee ID on [ae7q.com](http://ae7q.com/)."""
|
||||||
|
with ctx.typing():
|
||||||
|
query = query.upper()
|
||||||
|
|
||||||
|
# LID
|
||||||
|
if re.match(r"L\d+", query):
|
||||||
|
data = ae7qparser.get_licensee_id(query)
|
||||||
|
# FRN
|
||||||
|
elif re.match(r"\d{10}", query):
|
||||||
|
data = ae7qparser.get_frn(query)
|
||||||
|
# callsign
|
||||||
|
else:
|
||||||
|
data = ae7qparser.get_call(query)
|
||||||
|
|
||||||
|
embed = cmn.embed_factory(ctx)
|
||||||
|
embed.title = f"AE7Q Pending Applications for {query}"
|
||||||
|
embed.url = data.query_url
|
||||||
|
embed.colour = cmn.colours.good
|
||||||
|
embed.description = ""
|
||||||
|
|
||||||
|
if not data.pending_applications:
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description += f"\nNo records found for `{query}`"
|
||||||
|
else:
|
||||||
|
# add the first three rows of the pending apps to the embed
|
||||||
|
if isinstance(data.pending_applications, ae7qparser.PendingApplicationsPredictionsTable):
|
||||||
|
for row in data.pending_applications[0:3]:
|
||||||
|
header = f"**{row.uls_file_number}** ({row.receipt_date:%Y-%m-%d})"
|
||||||
|
body = (f"Callsign: *{row.applicant_callsign}*\n"
|
||||||
|
f"Region: *{row.region_state}*\n"
|
||||||
|
f"Operator Class: *{row.operator_class}*\n"
|
||||||
|
f"Vanity Type: *{row.vanity_type}*\n")
|
||||||
|
if row.process_date:
|
||||||
|
body += f"Processes On: *{row.process_date:%Y-%m-%d}*\n"
|
||||||
|
body += (f"Sequential #: *{row.sequential_number}*\n"
|
||||||
|
f"Vanity Callsign: *{row.vanity_callsign}*\n"
|
||||||
|
f"Prediction: *{row.prediction}*")
|
||||||
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
|
elif isinstance(data.pending_applications, ae7qparser.CallsignPendingApplicationsPredictionsTable):
|
||||||
|
for row in data.pending_applications[0:3]:
|
||||||
|
header = f"**{row.uls_file_number}** ({row.receipt_date:%Y-%m-%d})"
|
||||||
|
body = (f"Callsign: *{row.applicant_callsign}*\n"
|
||||||
|
f"Region: *{row.region_state}*\n"
|
||||||
|
f"Operator Class: *{row.operator_class}*\n"
|
||||||
|
f"Vanity Type: *{row.vanity_type}*\n")
|
||||||
|
if row.process_date:
|
||||||
|
body += f"Processes On: *{row.process_date:%Y-%m-%d}*\n"
|
||||||
|
body += (f"Sequential #: *{row.sequential_number}*\n"
|
||||||
|
f"Prediction: *{row.prediction}*")
|
||||||
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
|
if len(data.pending_applications) > 3:
|
||||||
|
embed.description += (f"\nRecords 1 to 3 of {len(data.pending_applications)}. "
|
||||||
|
f"See [ae7q.com]({data.query_url}) for more...")
|
||||||
|
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
@_ae7q_lookup.command(name="app_detail", aliases=["ad"], category=cmn.cat.lookup)
|
||||||
|
async def _ae7q_app_detail(self, ctx: commands.Context, ufn: str):
|
||||||
|
"""Looks up the application data for a ULS file number on [ae7q.com](http://ae7q.com/)."""
|
||||||
|
with ctx.typing():
|
||||||
|
app_data = ae7qparser.get_application(ufn)
|
||||||
|
|
||||||
|
embed = cmn.embed_factory(ctx)
|
||||||
|
embed.title = f"AE7Q Application Data for {ufn}"
|
||||||
|
embed.url = app_data.query_url
|
||||||
|
embed.colour = cmn.colours.good
|
||||||
|
|
||||||
|
if not app_data.application_data:
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description += f"\nNo records found for `{ufn}`"
|
||||||
|
else:
|
||||||
|
if app_data.frn:
|
||||||
|
embed.add_field(name="FRN", value=app_data.frn, inline=True)
|
||||||
|
if app_data.licensee_id:
|
||||||
|
embed.add_field(name="Licensee ID", value=app_data.licensee_id, inline=True)
|
||||||
|
if app_data.applicant_type:
|
||||||
|
embed.add_field(name="Applicant Type", value=app_data.applicant_type, inline=True)
|
||||||
|
if app_data.entity_type:
|
||||||
|
embed.add_field(name="Entity Type", value=app_data.entity_type, inline=True)
|
||||||
|
if app_data.entity_name:
|
||||||
|
embed.add_field(name="Name", value=app_data.entity_name, inline=True)
|
||||||
|
if app_data.callsign:
|
||||||
|
embed.add_field(name="Callsign", value=app_data.callsign, inline=True)
|
||||||
|
|
||||||
|
address = f"ATTN: {app_data.attention}\n" if app_data.attention else ""
|
||||||
|
address += f"{app_data.street_address}\n" if app_data.street_address else ""
|
||||||
|
address += f"{app_data.po_box}\n" if app_data.po_box else ""
|
||||||
|
address += (f"{', '.join([app_data.locality, app_data.state.split('-')[0].strip()])}"
|
||||||
|
f" {app_data.postal_code}")
|
||||||
|
if address:
|
||||||
|
embed.add_field(name="Address", value=address, inline=True)
|
||||||
|
if app_data.county:
|
||||||
|
embed.add_field(name="County", value=app_data.county, inline=True)
|
||||||
|
if app_data.maidenhead:
|
||||||
|
embed.add_field(name="Grid Square", value=app_data.maidenhead, inline=True)
|
||||||
|
if app_data.uls_geo_region:
|
||||||
|
embed.add_field(name="Region", value=app_data.uls_geo_region, inline=True)
|
||||||
|
if app_data.last_action_date:
|
||||||
|
embed.add_field(name="Last Action", value=f"{app_data.last_action_date:%Y-%m-%d}", inline=True)
|
||||||
|
if app_data.receipt_date:
|
||||||
|
embed.add_field(name="Receipt Date", value=f"{app_data.receipt_date:%Y-%m-%d}", inline=True)
|
||||||
|
if app_data.entered_timestamp:
|
||||||
|
embed.add_field(name="Entered Time", value=f"{app_data.entered_timestamp:%Y-%m-%d}", inline=True)
|
||||||
|
if app_data.application_source:
|
||||||
|
embed.add_field(name="Application Source", value=app_data.application_source, inline=True)
|
||||||
|
if app_data.application_purpose:
|
||||||
|
embed.add_field(name="Purpose", value=app_data.application_purpose, inline=True)
|
||||||
|
if app_data.result:
|
||||||
|
embed.add_field(name="Result", value=app_data.result, inline=True)
|
||||||
|
if app_data.fee_control_number:
|
||||||
|
embed.add_field(name="Fee Control Number", value=app_data.fee_control_number, inline=True)
|
||||||
|
if app_data.payment_date:
|
||||||
|
embed.add_field(name="Payment Date", value=f"{app_data.payment_date:%Y-%m-%d}", inline=True)
|
||||||
|
if app_data.operator_class:
|
||||||
|
embed.add_field(name="Operator Class", value=app_data.operator_class, inline=True)
|
||||||
|
if app_data.operator_group:
|
||||||
|
embed.add_field(name="Operator Group", value=app_data.operator_group, inline=True)
|
||||||
|
if app_data.uls_group:
|
||||||
|
embed.add_field(name="ULS Group", value=app_data.uls_group, inline=True)
|
||||||
|
if app_data.vanity_type:
|
||||||
|
embed.add_field(name="Vanity Type", value=app_data.vanity_type, inline=True)
|
||||||
|
if app_data.vanity_relationship:
|
||||||
|
embed.add_field(name="Vanity Relationship", value=app_data.vanity_relationship, inline=True)
|
||||||
|
if app_data.trustee_name and app_data.trustee_callsign:
|
||||||
|
embed.add_field(name="Trustee",
|
||||||
|
value=f"{app_data.trustee_name} ({app_data.trustee_callsign})",
|
||||||
|
inline=True)
|
||||||
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
"""
|
|
||||||
raise NotImplementedError("Application history lookup not yet supported. "
|
|
||||||
"Check back in a later version of the bot.")
|
|
||||||
|
|
||||||
@_ae7q_lookup.command(name="frn", aliases=["f"], category=cmn.cat.lookup)
|
@_ae7q_lookup.command(name="frn", aliases=["f"], category=cmn.cat.lookup)
|
||||||
async def _ae7q_frn(self, ctx: commands.Context, frn: str):
|
async def _ae7q_frn(self, ctx: commands.Context, frn: str):
|
||||||
"""Looks up the history of an FRN on [ae7q.com](http://ae7q.com/)."""
|
"""Looks up the history of an FRN on [ae7q.com](http://ae7q.com/)."""
|
||||||
"""
|
|
||||||
NOTES:
|
|
||||||
- 2 tables: callsign history and application history
|
|
||||||
- If not found: no tables
|
|
||||||
"""
|
|
||||||
with ctx.typing():
|
with ctx.typing():
|
||||||
base_url = "http://ae7q.com/query/data/FrnHistory.php?FRN="
|
frn = frn.upper()
|
||||||
embed = cmn.embed_factory(ctx)
|
|
||||||
|
|
||||||
async with self.session.get(base_url + frn) as resp:
|
frn_data = ae7qparser.get_frn(frn)
|
||||||
if resp.status != 200:
|
|
||||||
raise cmn.BotHTTPError(resp)
|
|
||||||
page = await resp.text()
|
|
||||||
|
|
||||||
soup = BeautifulSoup(page, features="html.parser")
|
|
||||||
tables = [[row for row in table.find_all("tr")] for table in soup.select("table.Database")]
|
|
||||||
|
|
||||||
if not len(tables):
|
|
||||||
embed.title = f"AE7Q History for FRN {frn}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + frn
|
|
||||||
embed.description = f"No records found for FRN `{frn}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table = tables[0]
|
|
||||||
|
|
||||||
table_headers = table[0].find_all("th")
|
|
||||||
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
|
|
||||||
|
|
||||||
# catch if the wrong table was selected
|
|
||||||
if first_header is None or not first_header.startswith("With Licensee"):
|
|
||||||
embed.title = f"AE7Q History for FRN {frn}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + frn
|
|
||||||
embed.description = f"No records found for FRN `{frn}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table = await process_table(table[2:])
|
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"AE7Q History for FRN {frn}"
|
embed.title = f"AE7Q FRN History for {frn}"
|
||||||
|
embed.url = frn_data.query_url
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
embed.url = base_url + frn
|
embed.description = ""
|
||||||
|
|
||||||
# add the first three rows of the table to the embed
|
if not frn_data.frn_history:
|
||||||
for row in table[0:3]:
|
embed.colour = cmn.colours.bad
|
||||||
header = f"**{row[0]}** ({row[3]})" # **Callsign** (Applicant Type)
|
embed.description += f"\nNo records found for `{frn}`"
|
||||||
body = (f"Name: *{row[2]}*\n"
|
else:
|
||||||
f"Class: *{row[4]}*\n"
|
# add the first three rows of the FRN history to the embed
|
||||||
f"Region: *{row[1]}*\n"
|
for row in frn_data.frn_history[0:3]:
|
||||||
f"Status: *{row[5]}*\n"
|
header = f"**{row.callsign}** ({row.applicant_type})"
|
||||||
f"Granted: *{row[6]}*\n"
|
body = (f"Name: *{row.entity_name}*\n"
|
||||||
f"Effective: *{row[7]}*\n"
|
f"Region: *{row.region_state}*\n"
|
||||||
f"Cancelled: *{row[8]}*\n"
|
f"Operator Class: *{row.operator_class}*\n"
|
||||||
f"Expires: *{row[9]}*")
|
f"Status: *{row.license_status}*\n")
|
||||||
|
if row.grant_date:
|
||||||
|
body += f"Granted: *{row.grant_date:%Y-%m-%d}*\n"
|
||||||
|
if row.effective_date:
|
||||||
|
body += f"Effective: *{row.effective_date:%Y-%m-%d}*\n"
|
||||||
|
if row.cancel_date:
|
||||||
|
body += f"Cancelled: *{row.cancel_date:%Y-%m-%d}*\n"
|
||||||
|
if row.expire_date:
|
||||||
|
body += f"Expires: *{row.expire_date:%Y-%m-%d}*\n"
|
||||||
embed.add_field(name=header, value=body, inline=False)
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
if len(table) > 3:
|
if len(frn_data.frn_history) > 3:
|
||||||
embed.description = f"Records 1 to 3 of {len(table)}. See ae7q.com for more..."
|
embed.description += (f"\nRecords 1 to 3 of {len(frn_data.frn_history)}. "
|
||||||
|
f"See [ae7q.com]({frn_data.query_url}) for more...")
|
||||||
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@@ -310,87 +415,43 @@ class AE7QCog(commands.Cog):
|
|||||||
"""Looks up the history of a licensee ID on [ae7q.com](http://ae7q.com/)."""
|
"""Looks up the history of a licensee ID on [ae7q.com](http://ae7q.com/)."""
|
||||||
with ctx.typing():
|
with ctx.typing():
|
||||||
licensee_id = licensee_id.upper()
|
licensee_id = licensee_id.upper()
|
||||||
base_url = "http://ae7q.com/query/data/LicenseeIdHistory.php?ID="
|
|
||||||
embed = cmn.embed_factory(ctx)
|
|
||||||
|
|
||||||
async with self.session.get(base_url + licensee_id) as resp:
|
lid_data = ae7qparser.get_licensee_id(licensee_id)
|
||||||
if resp.status != 200:
|
|
||||||
raise cmn.BotHTTPError(resp)
|
|
||||||
page = await resp.text()
|
|
||||||
|
|
||||||
soup = BeautifulSoup(page, features="html.parser")
|
|
||||||
tables = [[row for row in table.find_all("tr")] for table in soup.select("table.Database")]
|
|
||||||
|
|
||||||
if not len(tables):
|
|
||||||
embed.title = f"AE7Q History for Licensee {licensee_id}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + licensee_id
|
|
||||||
embed.description = f"No records found for Licensee `{licensee_id}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table = tables[0]
|
|
||||||
|
|
||||||
table_headers = table[0].find_all("th")
|
|
||||||
first_header = "".join(table_headers[0].strings) if len(table_headers) > 0 else None
|
|
||||||
|
|
||||||
# catch if the wrong table was selected
|
|
||||||
if first_header is None or not first_header.startswith("With FCC"):
|
|
||||||
embed.title = f"AE7Q History for Licensee {licensee_id}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.url = base_url + licensee_id
|
|
||||||
embed.description = f"No records found for Licensee `{licensee_id}`"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
|
|
||||||
table = await process_table(table[2:])
|
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"AE7Q History for Licensee {licensee_id}"
|
embed.title = f"AE7Q History for Licensee {licensee_id}"
|
||||||
|
embed.url = lid_data.query_url
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
embed.url = base_url + licensee_id
|
embed.description = ""
|
||||||
|
|
||||||
|
if not lid_data.licensee_id_history:
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description += f"\nNo records found for `{licensee_id}`"
|
||||||
|
else:
|
||||||
|
# add the first three rows of the FRN history to the embed
|
||||||
|
for row in lid_data.licensee_id_history[0:3]:
|
||||||
|
header = f"**{row.callsign}** ({row.applicant_type})"
|
||||||
|
body = (f"Name: *{row.entity_name}*\n"
|
||||||
|
f"Region: *{row.region_state}*\n"
|
||||||
|
f"Operator Class: *{row.operator_class}*\n"
|
||||||
|
f"Status: *{row.license_status}*\n")
|
||||||
|
if row.grant_date:
|
||||||
|
body += f"Granted: *{row.grant_date:%Y-%m-%d}*\n"
|
||||||
|
if row.effective_date:
|
||||||
|
body += f"Effective: *{row.effective_date:%Y-%m-%d}*\n"
|
||||||
|
if row.cancel_date:
|
||||||
|
body += f"Cancelled: *{row.cancel_date:%Y-%m-%d}*\n"
|
||||||
|
if row.expire_date:
|
||||||
|
body += f"Expires: *{row.expire_date:%Y-%m-%d}*\n"
|
||||||
|
|
||||||
# add the first three rows of the table to the embed
|
|
||||||
for row in table[0:3]:
|
|
||||||
header = f"**{row[0]}** ({row[3]})" # **Callsign** (Applicant Type)
|
|
||||||
body = (f"Name: *{row[2]}*\n"
|
|
||||||
f"Class: *{row[4]}*\n"
|
|
||||||
f"Region: *{row[1]}*\n"
|
|
||||||
f"Status: *{row[5]}*\n"
|
|
||||||
f"Granted: *{row[6]}*\n"
|
|
||||||
f"Effective: *{row[7]}*\n"
|
|
||||||
f"Cancelled: *{row[8]}*\n"
|
|
||||||
f"Expires: *{row[9]}*")
|
|
||||||
embed.add_field(name=header, value=body, inline=False)
|
embed.add_field(name=header, value=body, inline=False)
|
||||||
|
|
||||||
if len(table) > 3:
|
if len(lid_data.licensee_id_history) > 3:
|
||||||
embed.description = f"Records 1 to 3 of {len(table)}. See ae7q.com for more..."
|
embed.description += (f"\nRecords 1 to 3 of {len(lid_data.licensee_id_history)}. "
|
||||||
|
f"See [ae7q.com]({lid_data.query_url}) for more...")
|
||||||
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
async def process_table(table: list):
|
|
||||||
"""Processes tables (*not* including headers) and returns the processed table"""
|
|
||||||
table_contents = []
|
|
||||||
for tr in table:
|
|
||||||
row = []
|
|
||||||
for td in tr.find_all("td"):
|
|
||||||
cell_val = td.getText().strip()
|
|
||||||
row.append(cell_val if cell_val else "-")
|
|
||||||
|
|
||||||
# take care of columns that span multiple rows by copying the contents rightward
|
|
||||||
if "colspan" in td.attrs and int(td.attrs["colspan"]) > 1:
|
|
||||||
for i in range(int(td.attrs["colspan"]) - 1):
|
|
||||||
row.append(row[-1])
|
|
||||||
|
|
||||||
# get rid of ditto marks by copying the contents from the previous row
|
|
||||||
for i, cell in enumerate(row):
|
|
||||||
if cell == "\"":
|
|
||||||
row[i] = table_contents[-1][i]
|
|
||||||
# add row to table
|
|
||||||
table_contents += [row]
|
|
||||||
return table_contents
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot: commands.Bot):
|
def setup(bot: commands.Bot):
|
||||||
bot.add_cog(AE7QCog(bot))
|
bot.add_cog(AE7QCog(bot))
|
||||||
|
|||||||
+1
-2
@@ -10,7 +10,6 @@ the GNU General Public License, version 2.
|
|||||||
|
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
from collections import OrderedDict
|
|
||||||
from typing import Union
|
from typing import Union
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
@@ -189,7 +188,7 @@ class BaseCog(commands.Cog):
|
|||||||
|
|
||||||
|
|
||||||
def parse_changelog():
|
def parse_changelog():
|
||||||
changelog = OrderedDict()
|
changelog = {}
|
||||||
ver = ""
|
ver = ""
|
||||||
heading = ""
|
heading = ""
|
||||||
|
|
||||||
|
|||||||
+5
-1
@@ -31,6 +31,11 @@ class FunCog(commands.Cog):
|
|||||||
"""Returns xkcd: tar."""
|
"""Returns xkcd: tar."""
|
||||||
await ctx.send("http://xkcd.com/1168")
|
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="xd", hidden=True, category=cmn.cat.fun)
|
@commands.command(name="xd", hidden=True, category=cmn.cat.fun)
|
||||||
async def _xd(self, ctx: commands.Context):
|
async def _xd(self, ctx: commands.Context):
|
||||||
"""ecks dee"""
|
"""ecks dee"""
|
||||||
@@ -39,7 +44,6 @@ class FunCog(commands.Cog):
|
|||||||
@commands.command(name="funetics", aliases=["fun"], category=cmn.cat.fun)
|
@commands.command(name="funetics", aliases=["fun"], category=cmn.cat.fun)
|
||||||
async def _funetics_lookup(self, ctx: commands.Context, *, msg: str):
|
async def _funetics_lookup(self, ctx: commands.Context, *, msg: str):
|
||||||
"""Generates fun/wacky phonetics for a word or phrase."""
|
"""Generates fun/wacky phonetics for a word or phrase."""
|
||||||
with ctx.typing():
|
|
||||||
result = ""
|
result = ""
|
||||||
for char in msg.lower():
|
for char in msg.lower():
|
||||||
if char.isalpha():
|
if char.isalpha():
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ class GridCog(commands.Cog):
|
|||||||
async def _grid_sq_lookup(self, ctx: commands.Context, lat: str, lon: str):
|
async def _grid_sq_lookup(self, ctx: commands.Context, lat: str, lon: str):
|
||||||
("""Calculates the grid square for latitude and longitude coordinates, """
|
("""Calculates the grid square for latitude and longitude coordinates, """
|
||||||
"""with negative being latitude South and longitude West.""")
|
"""with negative being latitude South and longitude West.""")
|
||||||
with ctx.typing():
|
|
||||||
grid = "**"
|
grid = "**"
|
||||||
latf = float(lat) + 90
|
latf = float(lat) + 90
|
||||||
lonf = float(lon) + 180
|
lonf = float(lon) + 180
|
||||||
@@ -53,7 +52,6 @@ class GridCog(commands.Cog):
|
|||||||
async def _location_lookup(self, ctx: commands.Context, grid: str, grid2: str = None):
|
async def _location_lookup(self, ctx: commands.Context, grid: str, grid2: str = None):
|
||||||
"""Calculates the latitude and longitude for the center of a grid square.
|
"""Calculates the latitude and longitude for the center of a grid square.
|
||||||
If two grid squares are given, the distance and azimuth between them is calculated."""
|
If two grid squares are given, the distance and azimuth between them is calculated."""
|
||||||
with ctx.typing():
|
|
||||||
if grid2 is None or grid2 == "":
|
if grid2 is None or grid2 == "":
|
||||||
try:
|
try:
|
||||||
grid = grid.upper()
|
grid = grid.upper()
|
||||||
|
|||||||
+20
-3
@@ -25,7 +25,6 @@ class HamCog(commands.Cog):
|
|||||||
@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):
|
||||||
"""Looks up the meaning of a Q Code."""
|
"""Looks up the meaning of a Q Code."""
|
||||||
with ctx.typing():
|
|
||||||
qcode = qcode.upper()
|
qcode = qcode.upper()
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
if qcode in qcodes.qcodes:
|
if qcode in qcodes.qcodes:
|
||||||
@@ -40,7 +39,6 @@ class HamCog(commands.Cog):
|
|||||||
@commands.command(name="phonetics", aliases=["ph", "phoneticize", "phoneticise", "phone"], category=cmn.cat.ref)
|
@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):
|
||||||
"""Returns NATO phonetics for a word or phrase."""
|
"""Returns NATO phonetics for a word or phrase."""
|
||||||
with ctx.typing():
|
|
||||||
result = ""
|
result = ""
|
||||||
for char in msg.lower():
|
for char in msg.lower():
|
||||||
if char.isalpha():
|
if char.isalpha():
|
||||||
@@ -57,7 +55,6 @@ class HamCog(commands.Cog):
|
|||||||
@commands.command(name="utc", aliases=["z"], category=cmn.cat.ref)
|
@commands.command(name="utc", aliases=["z"], category=cmn.cat.ref)
|
||||||
async def _utc_lookup(self, ctx: commands.Context):
|
async def _utc_lookup(self, ctx: commands.Context):
|
||||||
"""Returns the current time in UTC."""
|
"""Returns the current time in UTC."""
|
||||||
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 = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
@@ -95,6 +92,26 @@ class HamCog(commands.Cog):
|
|||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
await ctx.send(embed=embed)
|
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):
|
def setup(bot: commands.Bot):
|
||||||
bot.add_cog(HamCog(bot))
|
bot.add_cog(HamCog(bot))
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ class LookupCog(commands.Cog):
|
|||||||
@commands.command(name="dxcc", aliases=["dx"], category=cmn.cat.lookup)
|
@commands.command(name="dxcc", aliases=["dx"], category=cmn.cat.lookup)
|
||||||
async def _dxcc_lookup(self, ctx: commands.Context, query: str):
|
async def _dxcc_lookup(self, ctx: commands.Context, query: str):
|
||||||
"""Gets DXCC info about a callsign prefix."""
|
"""Gets DXCC info about a callsign prefix."""
|
||||||
with ctx.typing():
|
|
||||||
query = query.upper()
|
query = query.upper()
|
||||||
full_query = query
|
full_query = query
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ class MorseCog(commands.Cog):
|
|||||||
@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):
|
||||||
"""Converts ASCII to international morse code."""
|
"""Converts ASCII to international morse code."""
|
||||||
with ctx.typing():
|
|
||||||
result = ""
|
result = ""
|
||||||
for char in msg.upper():
|
for char in msg.upper():
|
||||||
try:
|
try:
|
||||||
@@ -38,7 +37,6 @@ class MorseCog(commands.Cog):
|
|||||||
@commands.command(name="unmorse", aliases=["demorse", "uncw", "decw"], category=cmn.cat.ref)
|
@commands.command(name="unmorse", aliases=["demorse", "uncw", "decw"], category=cmn.cat.ref)
|
||||||
async def _unmorse(self, ctx: commands.Context, *, msg: str):
|
async def _unmorse(self, ctx: commands.Context, *, msg: str):
|
||||||
"""Converts international morse code to ASCII."""
|
"""Converts international morse code to ASCII."""
|
||||||
with ctx.typing():
|
|
||||||
result = ""
|
result = ""
|
||||||
msg0 = msg
|
msg0 = msg
|
||||||
msg = msg.split("/")
|
msg = msg.split("/")
|
||||||
@@ -60,7 +58,6 @@ class MorseCog(commands.Cog):
|
|||||||
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)
|
embed = cmn.embed_factory(ctx)
|
||||||
with ctx.typing():
|
|
||||||
msg = msg.upper()
|
msg = msg.upper()
|
||||||
weight = 0
|
weight = 0
|
||||||
for char in msg:
|
for char in msg:
|
||||||
|
|||||||
+19
-22
@@ -8,7 +8,6 @@ the GNU General Public License, version 2.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
from collections import OrderedDict
|
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
@@ -36,6 +35,7 @@ class QRZCog(commands.Cog):
|
|||||||
await ctx.send(f"http://qrz.com/db/{callsign}")
|
await ctx.send(f"http://qrz.com/db/{callsign}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
async with ctx.typing():
|
||||||
try:
|
try:
|
||||||
await qrz_test_session(self.key, self.session)
|
await qrz_test_session(self.key, self.session)
|
||||||
except ConnectionError:
|
except ConnectionError:
|
||||||
@@ -158,27 +158,24 @@ def qrz_process_info(data: dict):
|
|||||||
else:
|
else:
|
||||||
lotw = "Unknown"
|
lotw = "Unknown"
|
||||||
|
|
||||||
return OrderedDict([("Name", name),
|
return {"Name": name,
|
||||||
("Country", data.get("country", None)),
|
"Country": data.get("country", None),
|
||||||
("Address", address),
|
"Address": address,
|
||||||
("Grid Square", data.get("grid", None)),
|
"Grid Square": data.get("grid", None),
|
||||||
("County", data.get("county", None)),
|
"County": data.get("county", None),
|
||||||
("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),
|
||||||
("Expires", data.get("expdate", None)),
|
"Expires": data.get("expdate", None),
|
||||||
("Aliases", data.get("aliases", None)),
|
"Aliases": data.get("aliases", None),
|
||||||
("Previous Callsign", data.get("p_call", None)),
|
"Previous Callsign": data.get("p_call", None),
|
||||||
("License Class", data.get("class", None)),
|
"License Class": data.get("class", None),
|
||||||
("Trustee", data.get("trustee", None)),
|
"Trustee": data.get("trustee", None),
|
||||||
("eQSL?", eqsl),
|
"eQSL?": eqsl,
|
||||||
("Paper QSL?", mqsl),
|
"Paper QSL?": mqsl,
|
||||||
("LotW?", lotw),
|
"LotW?": lotw,
|
||||||
("QSL Info", data.get("qslmgr", None)),
|
"QSL Info": data.get("qslmgr", None),
|
||||||
("CQ Zone", data.get("cqzone", None)),
|
"Born": data.get("born", None)}
|
||||||
("ITU Zone", data.get("ituzone", None)),
|
|
||||||
("IOTA Designator", data.get("iota", None)),
|
|
||||||
("Born", data.get("born", None))])
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
def setup(bot):
|
||||||
|
|||||||
+1
-2
@@ -14,7 +14,6 @@ from datetime import datetime
|
|||||||
import asyncio
|
import asyncio
|
||||||
|
|
||||||
import aiohttp
|
import aiohttp
|
||||||
import pytz
|
|
||||||
|
|
||||||
import discord.ext.commands as commands
|
import discord.ext.commands as commands
|
||||||
|
|
||||||
@@ -134,7 +133,7 @@ class StudyCog(commands.Cog):
|
|||||||
" the answer will be revealed."),
|
" the answer will be revealed."),
|
||||||
inline=False)
|
inline=False)
|
||||||
if "image" in question:
|
if "image" in question:
|
||||||
image_url = f"https://hamstudy.org/_1330011/images/{pool.split('_',1)[1]}/{question['image']}"
|
image_url = f"https://hamstudy.org/images/{pool_meta['year']}/{question['image']}"
|
||||||
embed.set_image(url=image_url)
|
embed.set_image(url=image_url)
|
||||||
|
|
||||||
q_msg = await ctx.send(embed=embed)
|
q_msg = await ctx.send(embed=embed)
|
||||||
|
|||||||
+1
-1
@@ -40,7 +40,7 @@ class WeatherCog(commands.Cog):
|
|||||||
embed.set_image(url="attachment://condx.png")
|
embed.set_image(url="attachment://condx.png")
|
||||||
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"], case_insensitive=True, category=cmn.cat.weather)
|
||||||
async def _weather_conditions(self, ctx: commands.Context):
|
async def _weather_conditions(self, ctx: commands.Context):
|
||||||
"""Gets local weather conditions from [wttr.in](http://wttr.in/).
|
"""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."""
|
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/miaowware/qrm2"
|
contributing = "Check out the source on GitHub, contributions welcome: https://github.com/miaowware/qrm2"
|
||||||
release = "2.2.1"
|
release = "2.3.1"
|
||||||
bot_server = "https://discord.gg/Ntbg3J4"
|
bot_server = "https://discord.gg/Ntbg3J4"
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ loop = asyncio.get_event_loop()
|
|||||||
connector = loop.run_until_complete(conn.new_connector())
|
connector = loop.run_until_complete(conn.new_connector())
|
||||||
|
|
||||||
bot = commands.Bot(command_prefix=opt.prefix,
|
bot = commands.Bot(command_prefix=opt.prefix,
|
||||||
description=info.description,
|
case_insensitive=True,
|
||||||
help_command=commands.MinimalHelpCommand(),
|
description=info.description, help_command=commands.MinimalHelpCommand(),
|
||||||
loop=loop,
|
loop=loop,
|
||||||
connector=connector)
|
connector=connector)
|
||||||
|
|
||||||
@@ -82,7 +82,7 @@ async def _shutdown_bot(ctx: commands.Context):
|
|||||||
await bot.logout()
|
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)
|
@commands.check(cmn.check_if_owner)
|
||||||
async def _extctl(ctx: commands.Context):
|
async def _extctl(ctx: commands.Context):
|
||||||
"""Extension control commands.
|
"""Extension control commands.
|
||||||
|
|||||||
@@ -3,3 +3,4 @@ ctyparser
|
|||||||
beautifulsoup4
|
beautifulsoup4
|
||||||
lxml
|
lxml
|
||||||
pytz
|
pytz
|
||||||
|
ae7qparser
|
||||||
|
|||||||
+12
-13
@@ -8,35 +8,33 @@ the GNU General Public License, version 2.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
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 = {
|
||||||
|
"**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"
|
||||||
" AA-AL (2x2)\n"
|
" AA-AL (2x2)\n"
|
||||||
"*Except*\n"
|
"*Except*\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:** KA-KZ, NA-NZ, WA-WZ (2x2)\n"
|
"**Group B** (Advanced and Extra Only)": ("**Any:** KA-KZ, NA-NZ, WA-WZ (2x2)\n"
|
||||||
"*Except*\n"
|
"*Except*\n"
|
||||||
"**Alaska:** AL (2x2)\n"
|
"**Alaska:** AL (2x2)\n"
|
||||||
"**Caribbean:** KP (2x2)\n"
|
"**Caribbean:** KP (2x2)\n"
|
||||||
"**Pacific:** AH (2x2)")),
|
"**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"
|
||||||
"**Caribbean:** NP, WP (2x2)\n"
|
"**Caribbean:** NP, WP (2x2)\n"
|
||||||
"**Pacific:** KH, NH, WH (2x2)")),
|
"**Pacific:** KH, NH, WH (2x2)"),
|
||||||
("**Group D** (Any License Class)", ("**Any Region:** KA-KZ, WA-WZ (2x3)\n"
|
"**Group D** (Any License Class)": ("**Any Region:** KA-KZ, WA-WZ (2x3)\n"
|
||||||
"*Except*\n"
|
"*Except*\n"
|
||||||
"**Alaska:** KL, WL (2x3)\n"
|
"**Alaska:** KL, WL (2x3)\n"
|
||||||
"**Caribbean:** KP, WP (2x3)\n"
|
"**Caribbean:** KP, WP (2x3)\n"
|
||||||
"**Pacific:** KH, WH (2x3)")),
|
"**Pacific:** KH, WH (2x3)"),
|
||||||
("**Unavailable**", ("- KA2AA-KA9ZZ: US Army in Japan\n"
|
"**Unavailable**": ("- KA2AA-KA9ZZ: US Army in Japan\n"
|
||||||
"- KC4AAA-KC4AAF: NSF in Antartica\n"
|
"- KC4AAA-KC4AAF: NSF in Antartica\n"
|
||||||
"- KC4USA-KC4USZ: US Navy in Antartica\n"
|
"- KC4USA-KC4USZ: US Navy in Antartica\n"
|
||||||
"- KG4AA-KG4ZZ: US Navy in Guantanamo Bay\n"
|
"- KG4AA-KG4ZZ: US Navy in Guantanamo Bay\n"
|
||||||
@@ -50,7 +48,8 @@ us_calls = OrderedDict([("**Group A** (Extra Only)", ("**Any:** K, N, W (1x2)\n"
|
|||||||
"- Any 2x3 with AF, KF, NF, or WF prefix and suffix EMA: FEMA\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 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: Special Event"))])
|
"- 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)}
|
||||||
|
|||||||
@@ -36,3 +36,43 @@ phonetics = {
|
|||||||
"y": "yankee",
|
"y": "yankee",
|
||||||
"z": "zulu"
|
"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,
|
||||||
|
}
|
||||||
|
|||||||
+119
-44225
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user