mirror of
https://github.com/miaowware/qrm2.git
synced 2026-06-03 06:24:52 -04:00
Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dcbb7acab8 | |||
| 3803ce6045 | |||
| 8ca4911072 | |||
| 6e2468f04f | |||
| b17a8a1749 | |||
| 8dfa7001ef | |||
| f6ed8430b9 | |||
| d650cbd6c1 | |||
| 4d9f9d1b19 | |||
| 1c649aacc2 | |||
| 2049ca9fca | |||
| 38416d9050 | |||
| 8dcdc22fe4 | |||
| c57056e586 |
@@ -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
|
||||||
+20
-1
@@ -7,6 +7,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
|
||||||
|
## [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
|
||||||
|
### Fixed
|
||||||
|
- Fixed issue where some HamStudy pools will become unselectable.
|
||||||
|
|
||||||
|
|
||||||
## [2.2.0] - 2020-02-15
|
## [2.2.0] - 2020-02-15
|
||||||
### Added
|
### Added
|
||||||
- Added Trustee field to qrz command for club callsigns.
|
- Added Trustee field to qrz command for club callsigns.
|
||||||
@@ -76,7 +92,10 @@ 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.2.3...HEAD
|
||||||
|
[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
|
||||||
|
|||||||
@@ -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"
|
||||||
|
}
|
||||||
+1
-1
@@ -29,7 +29,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:
|
||||||
|
|||||||
+11
-12
@@ -39,18 +39,17 @@ 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():
|
result += random.choice([word for word in self.words if word[0] == char])
|
||||||
result += random.choice([word for word in self.words if word[0] == char])
|
else:
|
||||||
else:
|
result += char
|
||||||
result += char
|
result += " "
|
||||||
result += " "
|
embed = cmn.embed_factory(ctx)
|
||||||
embed = cmn.embed_factory(ctx)
|
embed.title = f"Funetics for {msg}"
|
||||||
embed.title = f"Funetics for {msg}"
|
embed.description = result.title()
|
||||||
embed.description = result.title()
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+75
-77
@@ -23,93 +23,91 @@ 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
|
if 0 <= latf <= 180 and 0 <= lonf <= 360:
|
||||||
if 0 <= latf <= 180 and 0 <= lonf <= 360:
|
grid += chr(ord("A") + int(lonf / 20))
|
||||||
grid += chr(ord("A") + int(lonf / 20))
|
grid += chr(ord("A") + int(latf / 10))
|
||||||
grid += chr(ord("A") + int(latf / 10))
|
grid += chr(ord("0") + int((lonf % 20)/2))
|
||||||
grid += chr(ord("0") + int((lonf % 20)/2))
|
grid += chr(ord("0") + int((latf % 10)/1))
|
||||||
grid += chr(ord("0") + int((latf % 10)/1))
|
grid += chr(ord("a") + int((lonf - (int(lonf/2)*2)) / (5/60)))
|
||||||
grid += chr(ord("a") + int((lonf - (int(lonf/2)*2)) / (5/60)))
|
grid += chr(ord("a") + int((latf - (int(latf/1)*1)) / (2.5/60)))
|
||||||
grid += chr(ord("a") + int((latf - (int(latf/1)*1)) / (2.5/60)))
|
grid += "**"
|
||||||
grid += "**"
|
embed = cmn.embed_factory(ctx)
|
||||||
embed = cmn.embed_factory(ctx)
|
embed.title = f"Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}"
|
||||||
embed.title = f"Maidenhead Grid Locator for {float(lat):.6f}, {float(lon):.6f}"
|
embed.description = grid
|
||||||
embed.description = grid
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
else:
|
||||||
else:
|
embed = cmn.embed_factory(ctx)
|
||||||
embed = cmn.embed_factory(ctx)
|
embed.title = f"Error generating grid square for {lat}, {lon}."
|
||||||
embed.title = f"Error generating grid square for {lat}, {lon}."
|
embed.description = """Coordinates out of range.
|
||||||
embed.description = """Coordinates out of range.
|
The valid ranges are:
|
||||||
The valid ranges are:
|
- Latitude: `-90` to `+90`
|
||||||
- Latitude: `-90` to `+90`
|
- Longitude: `-180` to `+180`"""
|
||||||
- Longitude: `-180` to `+180`"""
|
embed.colour = cmn.colours.bad
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.command(name="ungrid", aliases=["loc"], category=cmn.cat.maps)
|
@commands.command(name="ungrid", aliases=["loc"], category=cmn.cat.maps)
|
||||||
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()
|
loc = get_coords(grid)
|
||||||
loc = get_coords(grid)
|
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"Latitude and Longitude for {grid}"
|
embed.title = f"Latitude and Longitude for {grid}"
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
|
|
||||||
if len(grid) >= 6:
|
if len(grid) >= 6:
|
||||||
embed.description = f"**{loc[0]:.5f}, {loc[1]:.5f}**"
|
embed.description = f"**{loc[0]:.5f}, {loc[1]:.5f}**"
|
||||||
embed.url = f"https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}"
|
embed.url = f"https://www.openstreetmap.org/#map=13/{loc[0]:.5f}/{loc[1]:.5f}"
|
||||||
else:
|
else:
|
||||||
embed.description = f"**{loc[0]:.1f}, {loc[1]:.1f}**"
|
embed.description = f"**{loc[0]:.1f}, {loc[1]:.1f}**"
|
||||||
embed.url = f"https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}"
|
embed.url = f"https://www.openstreetmap.org/#map=10/{loc[0]:.1f}/{loc[1]:.1f}"
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"Error generating latitude and longitude for grid {grid}."
|
embed.title = f"Error generating latitude and longitude for grid {grid}."
|
||||||
embed.description = str(e)
|
embed.description = str(e)
|
||||||
embed.colour = cmn.colours.bad
|
embed.colour = cmn.colours.bad
|
||||||
else:
|
else:
|
||||||
radius = 6371
|
radius = 6371
|
||||||
try:
|
try:
|
||||||
grid = grid.upper()
|
grid = grid.upper()
|
||||||
grid2 = grid2.upper()
|
grid2 = grid2.upper()
|
||||||
loc = get_coords(grid)
|
loc = get_coords(grid)
|
||||||
loc2 = get_coords(grid2)
|
loc2 = get_coords(grid2)
|
||||||
# Haversine formula
|
# Haversine formula
|
||||||
d_lat = math.radians(loc2[0] - loc[0])
|
d_lat = math.radians(loc2[0] - loc[0])
|
||||||
d_lon = math.radians(loc2[1] - loc[1])
|
d_lon = math.radians(loc2[1] - loc[1])
|
||||||
a = (math.sin(d_lat/2) ** 2
|
a = (math.sin(d_lat/2) ** 2
|
||||||
+ math.cos(math.radians(loc[0]))
|
+ math.cos(math.radians(loc[0]))
|
||||||
* math.cos(math.radians(loc2[0]))
|
* math.cos(math.radians(loc2[0]))
|
||||||
* math.sin(d_lon/2) ** 2)
|
* math.sin(d_lon/2) ** 2)
|
||||||
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
|
||||||
d = radius * c
|
d = radius * c
|
||||||
d_mi = 0.6213712 * d
|
d_mi = 0.6213712 * d
|
||||||
|
|
||||||
# Bearing
|
# Bearing
|
||||||
y_dist = math.sin(math.radians(loc2[1]-loc[1])) * math.cos(math.radians(loc2[0]))
|
y_dist = math.sin(math.radians(loc2[1]-loc[1])) * math.cos(math.radians(loc2[0]))
|
||||||
x_dist = (math.cos(math.radians(loc[0]))
|
x_dist = (math.cos(math.radians(loc[0]))
|
||||||
* math.sin(math.radians(loc2[0]))
|
* math.sin(math.radians(loc2[0]))
|
||||||
- math.sin(math.radians(loc[0]))
|
- math.sin(math.radians(loc[0]))
|
||||||
* math.cos(math.radians(loc2[0]))
|
* math.cos(math.radians(loc2[0]))
|
||||||
* math.cos(math.radians(loc2[1] - loc[1])))
|
* math.cos(math.radians(loc2[1] - loc[1])))
|
||||||
bearing = (math.degrees(math.atan2(y_dist, x_dist)) + 360) % 360
|
bearing = (math.degrees(math.atan2(y_dist, x_dist)) + 360) % 360
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"Great Circle Distance and Bearing from {grid} to {grid2}"
|
embed.title = f"Great Circle Distance and Bearing from {grid} to {grid2}"
|
||||||
embed.description = f"**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°"
|
embed.description = f"**Distance:** {d:.1f} km ({d_mi:.1f} mi)\n**Bearing:** {bearing:.1f}°"
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
embed.title = f"Error generating great circle distance and bearing from {grid} and {grid2}."
|
embed.title = f"Error generating great circle distance and bearing from {grid} and {grid2}."
|
||||||
embed.description = str(e)
|
embed.description = str(e)
|
||||||
embed.colour = cmn.colours.bad
|
embed.colour = cmn.colours.bad
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+26
-29
@@ -25,45 +25,42 @@ 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:
|
embed.title = qcode
|
||||||
embed.title = qcode
|
embed.description = qcodes.qcodes[qcode]
|
||||||
embed.description = qcodes.qcodes[qcode]
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
else:
|
||||||
else:
|
embed.title = f"Q Code {qcode} not found"
|
||||||
embed.title = f"Q Code {qcode} not found"
|
embed.colour = cmn.colours.bad
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@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():
|
result += phonetics.phonetics[char]
|
||||||
result += phonetics.phonetics[char]
|
else:
|
||||||
else:
|
result += char
|
||||||
result += char
|
result += " "
|
||||||
result += " "
|
embed = cmn.embed_factory(ctx)
|
||||||
embed = cmn.embed_factory(ctx)
|
embed.title = f"Phonetics for {msg}"
|
||||||
embed.title = f"Phonetics for {msg}"
|
embed.description = result.title()
|
||||||
embed.description = result.title()
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.command(name="utc", aliases=["z"], category=cmn.cat.ref)
|
@commands.command(name="utc", aliases=["z"], category=cmn.cat.ref)
|
||||||
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)
|
embed.title = "The current time is:"
|
||||||
embed.title = "The current time is:"
|
embed.description = result
|
||||||
embed.description = result
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"], category=cmn.cat.ref)
|
@commands.command(name="prefixes", aliases=["vanity", "pfx", "vanities", "prefix"], category=cmn.cat.ref)
|
||||||
|
|||||||
+22
-23
@@ -40,30 +40,29 @@ 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)
|
embed.title = "DXCC Info for "
|
||||||
embed.title = "DXCC Info for "
|
embed.description = f"*Last Updated: {self.cty.formatted_version}*"
|
||||||
embed.description = f"*Last Updated: {self.cty.formatted_version}*"
|
embed.colour = cmn.colours.bad
|
||||||
embed.colour = cmn.colours.bad
|
while query:
|
||||||
while query:
|
if query in self.cty.keys():
|
||||||
if query in self.cty.keys():
|
data = self.cty[query]
|
||||||
data = self.cty[query]
|
embed.add_field(name="Entity", value=data["entity"])
|
||||||
embed.add_field(name="Entity", value=data["entity"])
|
embed.add_field(name="CQ Zone", value=data["cq"])
|
||||||
embed.add_field(name="CQ Zone", value=data["cq"])
|
embed.add_field(name="ITU Zone", value=data["itu"])
|
||||||
embed.add_field(name="ITU Zone", value=data["itu"])
|
embed.add_field(name="Continent", value=data["continent"])
|
||||||
embed.add_field(name="Continent", value=data["continent"])
|
embed.add_field(name="Time Zone",
|
||||||
embed.add_field(name="Time Zone",
|
value=f"+{data['tz']}" if data["tz"] > 0 else str(data["tz"]))
|
||||||
value=f"+{data['tz']}" if data["tz"] > 0 else str(data["tz"]))
|
embed.title += query
|
||||||
embed.title += query
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
break
|
||||||
break
|
|
||||||
else:
|
|
||||||
query = query[:-1]
|
|
||||||
else:
|
else:
|
||||||
embed.title += full_query + " not found"
|
query = query[:-1]
|
||||||
embed.colour = cmn.colours.bad
|
else:
|
||||||
|
embed.title += full_query + " not found"
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@tasks.loop(hours=24)
|
@tasks.loop(hours=24)
|
||||||
|
|||||||
+41
-44
@@ -21,61 +21,58 @@ 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:
|
result += morse.morse[char]
|
||||||
result += morse.morse[char]
|
except KeyError:
|
||||||
except KeyError:
|
result += "<?>"
|
||||||
result += "<?>"
|
result += " "
|
||||||
result += " "
|
embed = cmn.embed_factory(ctx)
|
||||||
embed = cmn.embed_factory(ctx)
|
embed.title = f"Morse Code for {msg}"
|
||||||
embed.title = f"Morse Code for {msg}"
|
embed.description = "**" + result + "**"
|
||||||
embed.description = "**" + result + "**"
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.command(name="unmorse", aliases=["demorse", "uncw", "decw"], category=cmn.cat.ref)
|
@commands.command(name="unmorse", aliases=["demorse", "uncw", "decw"], category=cmn.cat.ref)
|
||||||
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("/")
|
msg = [m.split() for m in msg]
|
||||||
msg = [m.split() for m in msg]
|
for word in msg:
|
||||||
for word in msg:
|
for char in word:
|
||||||
for char in word:
|
try:
|
||||||
try:
|
result += morse.ascii[char]
|
||||||
result += morse.ascii[char]
|
except KeyError:
|
||||||
except KeyError:
|
result += "<?>"
|
||||||
result += "<?>"
|
result += " "
|
||||||
result += " "
|
embed = cmn.embed_factory(ctx)
|
||||||
embed = cmn.embed_factory(ctx)
|
embed.title = f"ASCII for {msg0}"
|
||||||
embed.title = f"ASCII for {msg0}"
|
embed.description = result
|
||||||
embed.description = result
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
@commands.command(name="cwweight", aliases=["weight", "cww"], category=cmn.cat.ref)
|
@commands.command(name="cwweight", aliases=["weight", "cww"], category=cmn.cat.ref)
|
||||||
async def _weight(self, ctx: commands.Context, *, msg: str):
|
async def _weight(self, ctx: commands.Context, *, msg: str):
|
||||||
"""Calculates the CW weight of a callsign or message."""
|
"""Calculates the CW weight of a callsign or message."""
|
||||||
embed = cmn.embed_factory(ctx)
|
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:
|
try:
|
||||||
try:
|
cw_char = morse.morse[char].replace("-", "==")
|
||||||
cw_char = morse.morse[char].replace("-", "==")
|
weight += len(cw_char) * 2 + 2
|
||||||
weight += len(cw_char) * 2 + 2
|
except KeyError:
|
||||||
except KeyError:
|
embed.title = "Error in calculation of CW weight"
|
||||||
embed.title = "Error in calculation of CW weight"
|
embed.description = f"Unknown character `{char}` in message"
|
||||||
embed.description = f"Unknown character `{char}` in message"
|
embed.colour = cmn.colours.bad
|
||||||
embed.colour = cmn.colours.bad
|
await ctx.send(embed=embed)
|
||||||
await ctx.send(embed=embed)
|
return
|
||||||
return
|
embed.title = f"CW Weight of {msg}"
|
||||||
embed.title = f"CW Weight of {msg}"
|
embed.description = f"The CW weight is **{weight}**"
|
||||||
embed.description = f"The CW weight is **{weight}**"
|
embed.colour = cmn.colours.good
|
||||||
embed.colour = cmn.colours.good
|
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+40
-39
@@ -36,50 +36,51 @@ class QRZCog(commands.Cog):
|
|||||||
await ctx.send(f"http://qrz.com/db/{callsign}")
|
await ctx.send(f"http://qrz.com/db/{callsign}")
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
async with ctx.typing():
|
||||||
await qrz_test_session(self.key, self.session)
|
try:
|
||||||
except ConnectionError:
|
await qrz_test_session(self.key, self.session)
|
||||||
await self.get_session()
|
except ConnectionError:
|
||||||
|
|
||||||
url = f"http://xmldata.qrz.com/xml/current/?s={self.key};callsign={callsign}"
|
|
||||||
async with self.session.get(url) as resp:
|
|
||||||
if resp.status != 200:
|
|
||||||
raise ConnectionError(f"Unable to connect to QRZ (HTTP Error {resp.status})")
|
|
||||||
with BytesIO(await resp.read()) as resp_file:
|
|
||||||
resp_xml = etree.parse(resp_file).getroot()
|
|
||||||
|
|
||||||
resp_xml_session = resp_xml.xpath("/x:QRZDatabase/x:Session", namespaces={"x": "http://xmldata.qrz.com"})
|
|
||||||
resp_session = {el.tag.split("}")[1]: el.text for el in resp_xml_session[0].getiterator()}
|
|
||||||
if "Error" in resp_session:
|
|
||||||
if "Session Timeout" in resp_session["Error"]:
|
|
||||||
await self.get_session()
|
await self.get_session()
|
||||||
await self._qrz_lookup(ctx, callsign)
|
|
||||||
return
|
|
||||||
if "Not found" in resp_session["Error"]:
|
|
||||||
embed = cmn.embed_factory(ctx)
|
|
||||||
embed.title = f"QRZ Data for {callsign.upper()}"
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
embed.description = "No data found!"
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
return
|
|
||||||
raise ValueError(resp_session["Error"])
|
|
||||||
|
|
||||||
resp_xml_data = resp_xml.xpath("/x:QRZDatabase/x:Callsign", namespaces={"x": "http://xmldata.qrz.com"})
|
url = f"http://xmldata.qrz.com/xml/current/?s={self.key};callsign={callsign}"
|
||||||
resp_data = {el.tag.split("}")[1]: el.text for el in resp_xml_data[0].getiterator()}
|
async with self.session.get(url) as resp:
|
||||||
|
if resp.status != 200:
|
||||||
|
raise ConnectionError(f"Unable to connect to QRZ (HTTP Error {resp.status})")
|
||||||
|
with BytesIO(await resp.read()) as resp_file:
|
||||||
|
resp_xml = etree.parse(resp_file).getroot()
|
||||||
|
|
||||||
embed = cmn.embed_factory(ctx)
|
resp_xml_session = resp_xml.xpath("/x:QRZDatabase/x:Session", namespaces={"x": "http://xmldata.qrz.com"})
|
||||||
embed.title = f"QRZ Data for {resp_data['call']}"
|
resp_session = {el.tag.split("}")[1]: el.text for el in resp_xml_session[0].getiterator()}
|
||||||
embed.colour = cmn.colours.good
|
if "Error" in resp_session:
|
||||||
embed.url = f"http://www.qrz.com/db/{resp_data['call']}"
|
if "Session Timeout" in resp_session["Error"]:
|
||||||
if "image" in resp_data:
|
await self.get_session()
|
||||||
embed.set_thumbnail(url=resp_data["image"])
|
await self._qrz_lookup(ctx, callsign)
|
||||||
|
return
|
||||||
|
if "Not found" in resp_session["Error"]:
|
||||||
|
embed = cmn.embed_factory(ctx)
|
||||||
|
embed.title = f"QRZ Data for {callsign.upper()}"
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
embed.description = "No data found!"
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
return
|
||||||
|
raise ValueError(resp_session["Error"])
|
||||||
|
|
||||||
data = qrz_process_info(resp_data)
|
resp_xml_data = resp_xml.xpath("/x:QRZDatabase/x:Callsign", namespaces={"x": "http://xmldata.qrz.com"})
|
||||||
|
resp_data = {el.tag.split("}")[1]: el.text for el in resp_xml_data[0].getiterator()}
|
||||||
|
|
||||||
for title, val in data.items():
|
embed = cmn.embed_factory(ctx)
|
||||||
if val is not None:
|
embed.title = f"QRZ Data for {resp_data['call']}"
|
||||||
embed.add_field(name=title, value=val, inline=True)
|
embed.colour = cmn.colours.good
|
||||||
await ctx.send(embed=embed)
|
embed.url = f"http://www.qrz.com/db/{resp_data['call']}"
|
||||||
|
if "image" in resp_data:
|
||||||
|
embed.set_thumbnail(url=resp_data["image"])
|
||||||
|
|
||||||
|
data = qrz_process_info(resp_data)
|
||||||
|
|
||||||
|
for title, val in data.items():
|
||||||
|
if val is not None:
|
||||||
|
embed.add_field(name=title, value=val, inline=True)
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
async def get_session(self):
|
async def get_session(self):
|
||||||
"""Session creation and caching."""
|
"""Session creation and caching."""
|
||||||
|
|||||||
+3
-3
@@ -88,8 +88,8 @@ class StudyCog(commands.Cog):
|
|||||||
else:
|
else:
|
||||||
# look at valid_from and expires dates to find the correct one
|
# look at valid_from and expires dates to find the correct one
|
||||||
for p in pool_matches:
|
for p in pool_matches:
|
||||||
valid_from = datetime.fromisoformat(pools[p]["valid_from"][:-1] + "+00:00")
|
valid_from = datetime.fromisoformat(pools[p]["valid_from"][:-1])
|
||||||
expires = datetime.fromisoformat(pools[p]["expires"][:-1] + "+00:00")
|
expires = datetime.fromisoformat(pools[p]["expires"][:-1])
|
||||||
|
|
||||||
if valid_from < datetime.utcnow() < expires:
|
if valid_from < datetime.utcnow() < expires:
|
||||||
pool = p
|
pool = p
|
||||||
@@ -133,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.0"
|
release = "2.2.3"
|
||||||
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.
|
||||||
|
|||||||
Reference in New Issue
Block a user