mirror of
https://github.com/miaowware/qrm2.git
synced 2026-06-02 14:04:50 -04:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6b0cdb6249 | |||
| 4eed94b55b | |||
| 3110961a3a | |||
| a4c8a056ac | |||
| 9368ccd9e2 | |||
| 8efd958314 | |||
| 4803bf89b2 | |||
| c82216cae6 | |||
| 1650cd50dc | |||
| 1b0b244f99 | |||
| 5db77f78d9 | |||
| c7ea5e0998 | |||
| adffd82127 | |||
| 970159e81b | |||
| f5aeefc934 | |||
| aac9262469 | |||
| b472cdfa25 | |||
| 585cae8b97 | |||
| c3fbd3e719 | |||
| 7eadb50b96 | |||
| 98642c099d |
@@ -15,6 +15,8 @@ jobs:
|
||||
docker:
|
||||
name: Build and push docker images
|
||||
runs-on: ubuntu-20.04
|
||||
permissions:
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
@@ -29,8 +31,8 @@ jobs:
|
||||
run: |
|
||||
IMAGE_ID=${GITHUB_REPOSITORY,,}
|
||||
IMAGE_NAME=${IMAGE_ID#*/}
|
||||
echo ::set-output name=image_id::$IMAGE_ID
|
||||
echo ::set-output name=image_name::$IMAGE_NAME
|
||||
echo "image_id=$IMAGE_ID" >> $GITHUB_ENV
|
||||
echo "image_name=$IMAGE_NAME" >> $GITHUB_ENV
|
||||
docker build . --file Dockerfile -t $IMAGE_NAME
|
||||
|
||||
- name: Login to Github Container Registry
|
||||
@@ -43,10 +45,10 @@ jobs:
|
||||
- name: Tag image
|
||||
id: tag_image
|
||||
run: |
|
||||
IMAGE_NAME=${{ steps.build_image.outputs.image_name }}
|
||||
IMAGE_ID=ghcr.io/${{ steps.build_image.outputs.image_id }}
|
||||
IMAGE_NAME=${{ env.image_name }}
|
||||
IMAGE_ID=ghcr.io/${{ env.image_id }}
|
||||
echo IMAGE_ID=$IMAGE_ID
|
||||
echo ::set-output name=image_id::$IMAGE_ID
|
||||
echo "image_id=$IMAGE_ID" >> $GITHUB_ENV
|
||||
|
||||
# Strip git ref prefix from version
|
||||
VERSION=$(echo "${{ github.ref }}" | sed -e 's,.*/\(.*\),\1,')
|
||||
@@ -55,7 +57,7 @@ jobs:
|
||||
# if version is master, set version to dev
|
||||
[[ "$VERSION" == "master" ]] && VERSION=dev
|
||||
echo VERSION=$VERSION
|
||||
echo ::set-output name=version::$VERSION
|
||||
echo "version=$VERSION" >> $GITHUB_ENV
|
||||
|
||||
# tag dev or x.x.x
|
||||
docker tag $IMAGE_NAME $IMAGE_ID:$VERSION
|
||||
@@ -64,8 +66,8 @@ jobs:
|
||||
|
||||
- name: Push images to registry
|
||||
run: |
|
||||
VERSION=${{ steps.tag_image.outputs.version }}
|
||||
IMAGE_ID=${{ steps.tag_image.outputs.image_id }}
|
||||
VERSION=${{ env.version }}
|
||||
IMAGE_ID=${{ env.image_id }}
|
||||
|
||||
[[ "$VERSION" != "dev" ]] && docker push $IMAGE_ID:latest || true
|
||||
docker push $IMAGE_ID:$VERSION
|
||||
@@ -77,4 +79,4 @@ jobs:
|
||||
url: ${{ secrets.DEPLOY_URL }}
|
||||
method: POST
|
||||
headers: '{"Authentication": "Token ${{ secrets.DEPLOY_TOKEN }}"}'
|
||||
payload: '{"version": "${{ steps.tag_image.outputs.version }}"}'
|
||||
payload: '{"version": "${{ env.version }}"}'
|
||||
|
||||
+12
-1
@@ -7,6 +7,16 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
## [Unreleased]
|
||||
|
||||
|
||||
## [2.9.0] - 2023-01-13
|
||||
### Changed
|
||||
- Migrated to Pycord.
|
||||
### Removed
|
||||
- Long-deprecated aliases for `?solarweather`.
|
||||
### Fixed
|
||||
- Issue where ?hamstudy would not work in direct messages (#442).
|
||||
- Issue where `?solarweather` would not show a picture (#461).
|
||||
|
||||
|
||||
## [2.8.0] - 2022-06-24
|
||||
### Removed
|
||||
- `?ae7q` command (#448).
|
||||
@@ -229,7 +239,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||
## 1.0.0 - 2019-07-31 [YANKED]
|
||||
|
||||
|
||||
[Unreleased]: https://github.com/miaowware/qrm2/compare/v2.8.0...HEAD
|
||||
[Unreleased]: https://github.com/miaowware/qrm2/compare/v2.9.0...HEAD
|
||||
[2.9.0]: https://github.com/miaowware/qrm2/releases/tag/v2.9.0
|
||||
[2.8.0]: https://github.com/miaowware/qrm2/releases/tag/v2.8.0
|
||||
[2.7.6]: https://github.com/miaowware/qrm2/releases/tag/v2.7.6
|
||||
[2.7.5]: https://github.com/miaowware/qrm2/releases/tag/v2.7.5
|
||||
|
||||
+4
-3
@@ -1,4 +1,4 @@
|
||||
FROM ghcr.io/void-linux/void-linux:latest-mini-x86_64
|
||||
FROM ghcr.io/void-linux/void-linux:latest-full-x86_64-musl
|
||||
LABEL org.opencontainers.image.source https://github.com/miaowware/qrm2
|
||||
|
||||
COPY . /app
|
||||
@@ -11,9 +11,10 @@ ARG GID 1000
|
||||
|
||||
RUN \
|
||||
echo "**** update system ****" && \
|
||||
xbps-install -SuyM -R ${REPOSITORY} && \
|
||||
xbps-install -Suy xbps -R ${REPOSITORY} && \
|
||||
xbps-install -uy -R ${REPOSITORY} && \
|
||||
echo "**** install system packages ****" && \
|
||||
xbps-install -yM -R ${REPOSITORY} ${PKGS} python3 python3-pip && \
|
||||
xbps-install -y -R ${REPOSITORY} ${PKGS} python3 python3-pip && \
|
||||
echo "**** install pip packages ****" && \
|
||||
pip3 install -U pip setuptools wheel && \
|
||||
pip3 install -r requirements.txt && \
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# Those are the defaults; they can be over-ridden if specified
|
||||
# at en environment level or as 'make' arguments.
|
||||
BOTENV ?= botenv
|
||||
PYTHON_BIN ?= python3.9
|
||||
PYTHON_BIN ?= python3.11
|
||||
PIP_OUTPUT ?= -q
|
||||
|
||||
|
||||
|
||||
@@ -18,10 +18,11 @@ from types import SimpleNamespace
|
||||
from typing import Union
|
||||
|
||||
import aiohttp
|
||||
import httpx
|
||||
|
||||
import discord
|
||||
import discord.ext.commands as commands
|
||||
from discord import Emoji, Reaction, PartialEmoji
|
||||
from discord import Emoji, PartialEmoji
|
||||
|
||||
import data.options as opt
|
||||
|
||||
@@ -125,12 +126,16 @@ class ImagesGroup(collections.abc.Mapping):
|
||||
|
||||
class BotHTTPError(Exception):
|
||||
"""Raised whan a requests fails (status != 200) in a command."""
|
||||
def __init__(self, response: aiohttp.ClientResponse):
|
||||
msg = f"Request failed: {response.status} {response.reason}"
|
||||
def __init__(self, response: aiohttp.ClientResponse | httpx.Response):
|
||||
if isinstance(response, aiohttp.ClientResponse):
|
||||
self.status = response.status
|
||||
self.reason = response.reason
|
||||
else:
|
||||
self.status = response.status_code
|
||||
self.reason = response.reason_phrase
|
||||
msg = f"Request failed: {self.status} {self.reason}"
|
||||
super().__init__(msg)
|
||||
self.response = response
|
||||
self.status = response.status
|
||||
self.reason = response.reason
|
||||
|
||||
|
||||
# --- Converters ---
|
||||
@@ -161,7 +166,8 @@ class GlobalChannelConverter(commands.IDConverter):
|
||||
def embed_factory(ctx: commands.Context) -> discord.Embed:
|
||||
"""Creates an embed with neutral colour and standard footer."""
|
||||
embed = discord.Embed(timestamp=datetime.utcnow(), colour=colours.neutral)
|
||||
embed.set_footer(text=str(ctx.author), icon_url=str(ctx.author.avatar_url))
|
||||
if ctx.author:
|
||||
embed.set_footer(text=str(ctx.author), icon_url=str(ctx.author.avatar.url))
|
||||
return embed
|
||||
|
||||
|
||||
@@ -178,7 +184,7 @@ def error_embed_factory(ctx: commands.Context, exception: Exception, debug_mode:
|
||||
return embed
|
||||
|
||||
|
||||
async def add_react(msg: discord.Message, react: Union[Emoji, Reaction, PartialEmoji, str]):
|
||||
async def add_react(msg: discord.Message, react: Union[Emoji, PartialEmoji, str]):
|
||||
try:
|
||||
await msg.add_reaction(react)
|
||||
except discord.Forbidden:
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
-r requirements.txt
|
||||
flake8
|
||||
discord.py-stubs==1.7.3
|
||||
mypy
|
||||
|
||||
+5
-3
@@ -141,7 +141,8 @@ class QrmHelpCommand(commands.HelpCommand):
|
||||
embed.title = await self.get_command_signature(group)
|
||||
embed.description = group.help
|
||||
for cmd in await self.filter_commands(group.commands, sort=True):
|
||||
embed.add_field(name=await self.get_command_signature(cmd), value=cmd.help, inline=False)
|
||||
embed.add_field(name=await self.get_command_signature(cmd), value=cmd.help if cmd.help else "",
|
||||
inline=False)
|
||||
await self.context.send(embed=embed)
|
||||
|
||||
|
||||
@@ -177,7 +178,7 @@ class BaseCog(commands.Cog):
|
||||
|
||||
@commands.Cog.listener()
|
||||
async def on_ready(self):
|
||||
if not self.bot_invite:
|
||||
if not self.bot_invite and self.bot.user:
|
||||
self.bot_invite = (f"https://discordapp.com/oauth2/authorize?client_id={self.bot.user.id}"
|
||||
f"&scope=bot&permissions={opt.invite_perms}")
|
||||
|
||||
@@ -196,7 +197,8 @@ class BaseCog(commands.Cog):
|
||||
inline=False)
|
||||
if opt.enable_invite_cmd and (await self.bot.application_info()).bot_public:
|
||||
embed.add_field(name="Invite qrm to Your Server", value=self.bot_invite, inline=False)
|
||||
embed.set_thumbnail(url=str(self.bot.user.avatar_url))
|
||||
if self.bot.user and self.bot.user.avatar:
|
||||
embed.set_thumbnail(url=str(self.bot.user.avatar.url))
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="ping", aliases=["beep"], category=cmn.BoltCats.INFO)
|
||||
|
||||
+41
-44
@@ -9,11 +9,10 @@ SPDX-License-Identifier: LiLiQ-Rplus-1.1
|
||||
|
||||
|
||||
from typing import Dict
|
||||
from datetime import datetime
|
||||
|
||||
import aiohttp
|
||||
from qrztools import qrztools, QrzAsync, QrzError
|
||||
from gridtools import Grid, LatLong
|
||||
from callsignlookuptools import QrzAsyncClient, CallsignLookupError, CallsignData
|
||||
from callsignlookuptools.common.dataclasses import Trustee
|
||||
|
||||
from discord.ext import commands
|
||||
|
||||
@@ -29,14 +28,16 @@ class QRZCog(commands.Cog):
|
||||
self.qrz = None
|
||||
try:
|
||||
if keys.qrz_user and keys.qrz_pass:
|
||||
self.qrz = QrzAsync(keys.qrz_user, keys.qrz_pass, useragent="discord-qrm2",
|
||||
session=aiohttp.ClientSession(connector=bot.qrm.connector))
|
||||
# seed the qrz object with the previous session key, in case it already works
|
||||
session_key = ""
|
||||
try:
|
||||
with open("data/qrz_session") as qrz_file:
|
||||
self.qrz.session_key = qrz_file.readline().strip()
|
||||
session_key = qrz_file.readline().strip()
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
self.qrz = QrzAsyncClient(username=keys.qrz_user, password=keys.qrz_pass, useragent="discord-qrm2",
|
||||
session_key=session_key,
|
||||
session=aiohttp.ClientSession(connector=bot.qrm.connector))
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@@ -63,69 +64,65 @@ class QRZCog(commands.Cog):
|
||||
|
||||
async with ctx.typing():
|
||||
try:
|
||||
data = await self.qrz.get_callsign(callsign)
|
||||
except QrzError as e:
|
||||
data = await self.qrz.search(callsign)
|
||||
except CallsignLookupError as e:
|
||||
embed.colour = cmn.colours.bad
|
||||
embed.description = str(e)
|
||||
await ctx.send(embed=embed)
|
||||
return
|
||||
|
||||
embed.title = f"QRZ Data for {data.call}"
|
||||
embed.title = f"QRZ Data for {data.callsign}"
|
||||
embed.colour = cmn.colours.good
|
||||
embed.url = data.url
|
||||
if data.image != qrztools.QrzImage():
|
||||
if data.image is not None and data.image.url is not None:
|
||||
embed.set_thumbnail(url=data.image.url)
|
||||
|
||||
for title, val in qrz_process_info(data).items():
|
||||
if val:
|
||||
if val is not None and (val := str(val)):
|
||||
embed.add_field(name=title, value=val, inline=True)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
|
||||
def qrz_process_info(data: qrztools.QrzCallsignData) -> Dict:
|
||||
if data.name != qrztools.Name():
|
||||
def qrz_process_info(data: CallsignData) -> Dict:
|
||||
if data.name is not None:
|
||||
if opt.qrz_only_nickname:
|
||||
if data.name.nickname:
|
||||
name = data.name.nickname + " " + data.name.name
|
||||
nm = data.name.name if data.name.name is not None else ""
|
||||
if data.name.nickname is not None:
|
||||
name = data.name.nickname + " " + nm
|
||||
elif data.name.first:
|
||||
name = data.name.first + " " + data.name.name
|
||||
name = data.name.first + " " + nm
|
||||
else:
|
||||
name = data.name.name
|
||||
name = nm
|
||||
else:
|
||||
name = data.name.formatted_name
|
||||
name = data.name
|
||||
else:
|
||||
name = None
|
||||
|
||||
if data.address != qrztools.Address():
|
||||
state = ", " + data.address.state + " " if data.address.state else ""
|
||||
address = "\n".join(
|
||||
[x for x
|
||||
in [data.address.attn, data.address.line1, data.address.line2 + state, data.address.zip]
|
||||
if x]
|
||||
)
|
||||
else:
|
||||
address = None
|
||||
qsl = dict()
|
||||
if data.qsl is not None:
|
||||
qsl = {
|
||||
"eQSL?": data.qsl.eqsl.name.title(),
|
||||
"Paper QSL?": data.qsl.mail.name.title(),
|
||||
"LotW?": data.qsl.lotw.name.title(),
|
||||
"QSL Info": data.qsl.info,
|
||||
}
|
||||
|
||||
return {
|
||||
"Name": name,
|
||||
"Country": data.address.country,
|
||||
"Address": address,
|
||||
"Grid Square": data.grid if data.grid != Grid(LatLong(0, 0)) else None,
|
||||
"County": data.county if data.county else None,
|
||||
"CQ Zone": data.cq_zone if data.cq_zone else None,
|
||||
"ITU Zone": data.itu_zone if data.itu_zone else None,
|
||||
"IOTA Designator": data.iota if data.iota else None,
|
||||
"Expires": f"{data.expire_date:%Y-%m-%d}" if data.expire_date != datetime.min else None,
|
||||
"Country": data.address.country if data.address is not None else None,
|
||||
"Address": data.address,
|
||||
"Grid Square": data.grid,
|
||||
"County": data.county,
|
||||
"CQ Zone": data.cq_zone,
|
||||
"ITU Zone": data.itu_zone,
|
||||
"IOTA Designator": data.iota,
|
||||
"Expires": f"{data.expire_date:%Y-%m-%d}" if data.expire_date is not None else None,
|
||||
"Aliases": ", ".join(data.aliases) if data.aliases else None,
|
||||
"Previous Callsign": data.prev_call if data.prev_call else None,
|
||||
"License Class": data.lic_class if data.lic_class else None,
|
||||
"Trustee": data.trustee if data.trustee else None,
|
||||
"eQSL?": "Yes" if data.eqsl else "No",
|
||||
"Paper QSL?": "Yes" if data.mail_qsl else "No",
|
||||
"LotW?": "Yes" if data.lotw_qsl else "No",
|
||||
"QSL Info": data.qsl_manager if data.qsl_manager else None,
|
||||
"Born": f"{data.born:%Y-%m-%d}" if data.born != datetime.min else None
|
||||
}
|
||||
"Previous Callsign": data.prev_call,
|
||||
"License Class": data.lic_class,
|
||||
"Trustee": data.trustee if data.trustee is not None and data.trustee != Trustee(None, None) else None,
|
||||
"Born": data.born,
|
||||
} | qsl
|
||||
|
||||
|
||||
def setup(bot):
|
||||
|
||||
+1
-1
@@ -187,7 +187,7 @@ def _calc_volt(db: float, ref: float):
|
||||
|
||||
# testing code
|
||||
if __name__ == "__main__":
|
||||
while(True):
|
||||
while True:
|
||||
try:
|
||||
ip = input("> ").split()
|
||||
initial = float(ip[0])
|
||||
|
||||
+22
-17
@@ -7,11 +7,11 @@ SPDX-License-Identifier: LiLiQ-Rplus-1.1
|
||||
"""
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
from io import BytesIO
|
||||
|
||||
import aiohttp
|
||||
import cairosvg
|
||||
from datetime import datetime
|
||||
import httpx
|
||||
|
||||
import discord
|
||||
import discord.ext.commands as commands
|
||||
@@ -27,15 +27,17 @@ class PropagationCog(commands.Cog):
|
||||
|
||||
def __init__(self, bot):
|
||||
self.bot = bot
|
||||
self.session = aiohttp.ClientSession(connector=bot.qrm.connector)
|
||||
self.httpx_client: httpx.AsyncClient = bot.qrm.httpx_client
|
||||
|
||||
@commands.command(name="mufmap", aliases=["muf"], category=cmn.Cats.WEATHER)
|
||||
async def mufmap(self, ctx: commands.Context):
|
||||
"""Shows a world map of the Maximum Usable Frequency (MUF)."""
|
||||
async with ctx.typing():
|
||||
async with self.session.get(self.muf_url, headers={"Connection": "Upgrade", "Upgrade": "http/1.1"}) as r:
|
||||
svg = await r.read()
|
||||
out = BytesIO(cairosvg.svg2png(bytestring=svg))
|
||||
resp = await self.httpx_client.get(self.muf_url)
|
||||
await resp.aclose()
|
||||
if resp.status_code != 200:
|
||||
raise cmn.BotHTTPError(resp)
|
||||
out = BytesIO(cairosvg.svg2png(bytestring=await resp.aread()))
|
||||
file = discord.File(out, "muf_map.png")
|
||||
embed = cmn.embed_factory(ctx)
|
||||
embed.title = "Maximum Usable Frequency Map"
|
||||
@@ -47,9 +49,11 @@ class PropagationCog(commands.Cog):
|
||||
async def fof2map(self, ctx: commands.Context):
|
||||
"""Shows a world map of the Critical Frequency (foF2)."""
|
||||
async with ctx.typing():
|
||||
async with self.session.get(self.fof2_url, headers={"Connection": "Upgrade", "Upgrade": "http/1.1"}) as r:
|
||||
svg = await r.read()
|
||||
out = BytesIO(cairosvg.svg2png(bytestring=svg))
|
||||
resp = await self.httpx_client.get(self.fof2_url)
|
||||
await resp.aclose()
|
||||
if resp.status_code != 200:
|
||||
raise cmn.BotHTTPError(resp)
|
||||
out = BytesIO(cairosvg.svg2png(bytestring=await resp.aread()))
|
||||
file = discord.File(out, "fof2_map.png")
|
||||
embed = cmn.embed_factory(ctx)
|
||||
embed.title = "Critical Frequency (foF2) Map"
|
||||
@@ -67,19 +71,20 @@ class PropagationCog(commands.Cog):
|
||||
embed.set_image(url=self.gl_baseurl + date_params)
|
||||
await ctx.send(embed=embed)
|
||||
|
||||
@commands.command(name="solarweather", aliases=["solar", "bandconditions", "cond", "condx", "conditions"],
|
||||
category=cmn.Cats.WEATHER)
|
||||
@commands.command(name="solarweather", aliases=["solar"], category=cmn.Cats.WEATHER)
|
||||
async def solarweather(self, ctx: commands.Context):
|
||||
"""Gets a solar weather report."""
|
||||
resp = await self.httpx_client.get(self.n0nbh_sun_url)
|
||||
await resp.aclose()
|
||||
if resp.status_code != 200:
|
||||
raise cmn.BotHTTPError(resp)
|
||||
img = BytesIO(await resp.aread())
|
||||
file = discord.File(img, "solarweather.png")
|
||||
embed = cmn.embed_factory(ctx)
|
||||
embed.title = "☀️ Current Solar Weather"
|
||||
if ctx.invoked_with in ["bandconditions", "cond", "condx", "conditions"]:
|
||||
embed.add_field(name="⚠️ Deprecated Command Alias",
|
||||
value=(f"This command has been renamed to `{ctx.prefix}solar`!\n"
|
||||
"The alias you used will be removed in the next version."))
|
||||
embed.colour = cmn.colours.good
|
||||
embed.set_image(url=self.n0nbh_sun_url)
|
||||
await ctx.send(embed=embed)
|
||||
embed.set_image(url="attachment://solarweather.png")
|
||||
await ctx.send(file=file, embed=embed)
|
||||
|
||||
|
||||
def setup(bot: commands.Bot):
|
||||
|
||||
+19
-13
@@ -159,13 +159,13 @@ class StudyCog(commands.Cog):
|
||||
await cmn.add_react(q_msg, list(self.choices.values())[i])
|
||||
await cmn.add_react(q_msg, cmn.emojis.question)
|
||||
|
||||
def check(reaction, user):
|
||||
return (user.id != self.bot.user.id
|
||||
and reaction.message.id == q_msg.id
|
||||
and (str(reaction.emoji) in self.choices.values() or str(reaction.emoji) == cmn.emojis.question))
|
||||
def check(ev):
|
||||
return (ev.user_id != self.bot.user.id
|
||||
and ev.message_id == q_msg.id
|
||||
and (str(ev.emoji) in self.choices.values() or str(ev.emoji) == cmn.emojis.question))
|
||||
|
||||
try:
|
||||
reaction, user = await self.bot.wait_for("reaction_add", timeout=300.0, check=check)
|
||||
ev = await self.bot.wait_for("raw_reaction_add", timeout=300.0, check=check)
|
||||
except asyncio.TimeoutError:
|
||||
embed.set_field_at(1, name="Answers", value=answers_str_bolded, inline=False)
|
||||
embed.set_field_at(2, name="Answer",
|
||||
@@ -174,16 +174,18 @@ class StudyCog(commands.Cog):
|
||||
embed.colour = cmn.colours.timeout
|
||||
await q_msg.edit(embed=embed)
|
||||
else:
|
||||
if str(reaction.emoji) == cmn.emojis.question:
|
||||
if str(ev.emoji) == cmn.emojis.question:
|
||||
embed.set_field_at(1, name="Answers", value=answers_str_bolded, inline=False)
|
||||
embed.set_field_at(2, name="Answer",
|
||||
value=f"The correct answer was {self.choices[question['answer']]}", inline=False)
|
||||
embed.add_field(name="Answer Requested By", value=str(user), inline=False)
|
||||
# only available in guilds, but it only makes sense there
|
||||
if ev.member:
|
||||
embed.add_field(name="Answer Requested By", value=str(ev.member), inline=False)
|
||||
embed.colour = cmn.colours.timeout
|
||||
await q_msg.edit(embed=embed)
|
||||
else:
|
||||
answers_str_checked = ""
|
||||
chosen_ans = self.choices_inv[str(reaction.emoji)]
|
||||
chosen_ans = self.choices_inv[str(ev.emoji)]
|
||||
for letter, ans in answers.items():
|
||||
answers_str_checked += f"{self.choices[letter]}"
|
||||
if letter == question["answer"] == chosen_ans:
|
||||
@@ -195,19 +197,23 @@ class StudyCog(commands.Cog):
|
||||
else:
|
||||
answers_str_checked += f" {ans}\n"
|
||||
|
||||
if self.choices[question["answer"]] == str(reaction.emoji):
|
||||
if self.choices[question["answer"]] == str(ev.emoji):
|
||||
embed.set_field_at(1, name="Answers", value=answers_str_checked, inline=False)
|
||||
embed.set_field_at(2, name="Answer", value=(f"{cmn.emojis.check_mark} "
|
||||
f"**Correct!** The answer was {reaction.emoji}"))
|
||||
embed.add_field(name="Answered By", value=str(user), inline=False)
|
||||
f"**Correct!** The answer was {ev.emoji}"))
|
||||
# only available in guilds, but it only makes sense there
|
||||
if ev.member:
|
||||
embed.add_field(name="Answered By", value=str(ev.member), inline=False)
|
||||
embed.colour = cmn.colours.good
|
||||
await q_msg.edit(embed=embed)
|
||||
else:
|
||||
embed.set_field_at(1, name="Answers", value=answers_str_checked, inline=False)
|
||||
embed.set_field_at(2, name="Answer",
|
||||
value=(f"{cmn.emojis.x} **Incorrect!** The correct answer was "
|
||||
f"{self.choices[question['answer']]}, not {reaction.emoji}"))
|
||||
embed.add_field(name="Answered By", value=str(user), inline=False)
|
||||
f"{self.choices[question['answer']]}, not {ev.emoji}"))
|
||||
# only available in guilds, but it only makes sense there
|
||||
if ev.member:
|
||||
embed.add_field(name="Answered By", value=str(ev.member), inline=False)
|
||||
embed.colour = cmn.colours.bad
|
||||
await q_msg.edit(embed=embed)
|
||||
|
||||
|
||||
@@ -14,5 +14,5 @@ contributing = """Check out the [source on GitHub](https://github.com/miaowware/
|
||||
|
||||
All issues and requests related to resources (including maps, band charts, data) should be added \
|
||||
in [miaowware/qrm-resources](https://github.com/miaowware/qrm-resources)."""
|
||||
release = "2.8.0"
|
||||
release = "2.9.0"
|
||||
bot_server = "https://discord.gg/Ntbg3J4"
|
||||
|
||||
@@ -16,6 +16,7 @@ from datetime import datetime, time
|
||||
from types import SimpleNamespace
|
||||
from pathlib import Path
|
||||
|
||||
import httpx
|
||||
import pytz
|
||||
|
||||
import discord
|
||||
@@ -49,9 +50,9 @@ connector = loop.run_until_complete(conn.new_connector())
|
||||
# Defining the intents
|
||||
intents = discord.Intents.none()
|
||||
intents.guilds = True
|
||||
intents.guild_messages = True
|
||||
intents.dm_messages = True
|
||||
intents.messages = True
|
||||
intents.reactions = True
|
||||
intents.message_content = True
|
||||
|
||||
member_cache = discord.MemberCacheFlags.from_intents(intents)
|
||||
|
||||
@@ -69,6 +70,8 @@ bot.qrm = SimpleNamespace()
|
||||
# Let's store stuff here.
|
||||
bot.qrm.connector = connector
|
||||
bot.qrm.debug_mode = debug_mode
|
||||
# TODO: Add code to close the client
|
||||
bot.qrm.httpx_client = httpx.AsyncClient()
|
||||
|
||||
|
||||
# --- Commands ---
|
||||
@@ -81,7 +84,7 @@ async def _restart_bot(ctx: commands.Context):
|
||||
await cmn.add_react(ctx.message, cmn.emojis.check_mark)
|
||||
print(f"[**] Restarting! Requested by {ctx.author}.")
|
||||
exit_code = 42 # Signals to the wrapper script that the bot needs to be restarted.
|
||||
await bot.logout()
|
||||
await bot.close()
|
||||
|
||||
|
||||
@bot.command(name="shutdown", aliases=["shut"], category=cmn.BoltCats.ADMIN)
|
||||
@@ -92,7 +95,7 @@ async def _shutdown_bot(ctx: commands.Context):
|
||||
await cmn.add_react(ctx.message, cmn.emojis.check_mark)
|
||||
print(f"[**] Shutting down! Requested by {ctx.author}.")
|
||||
exit_code = 0 # Signals to the wrapper script that the bot should not be restarted.
|
||||
await bot.logout()
|
||||
await bot.close()
|
||||
|
||||
|
||||
@bot.group(name="extctl", aliases=["ex"], case_insensitive=True, category=cmn.BoltCats.ADMIN)
|
||||
@@ -123,10 +126,10 @@ async def _extctl_load(ctx: commands.Context, extension: str):
|
||||
"""Loads an extension."""
|
||||
try:
|
||||
bot.load_extension(ext_dir + "." + extension)
|
||||
except commands.ExtensionNotFound as e:
|
||||
except discord.errors.ExtensionNotFound as e:
|
||||
try:
|
||||
bot.load_extension(plugin_dir + "." + extension)
|
||||
except commands.ExtensionNotFound:
|
||||
except discord.errors.ExtensionNotFound:
|
||||
raise e
|
||||
await cmn.add_react(ctx.message, cmn.emojis.check_mark)
|
||||
|
||||
@@ -140,10 +143,10 @@ async def _extctl_reload(ctx: commands.Context, extension: str):
|
||||
await cmn.add_react(ctx.message, pika)
|
||||
try:
|
||||
bot.reload_extension(ext_dir + "." + extension)
|
||||
except commands.ExtensionNotLoaded as e:
|
||||
except discord.errors.ExtensionNotLoaded as e:
|
||||
try:
|
||||
bot.reload_extension(plugin_dir + "." + extension)
|
||||
except commands.ExtensionNotLoaded:
|
||||
except discord.errors.ExtensionNotLoaded:
|
||||
raise e
|
||||
await cmn.add_react(ctx.message, cmn.emojis.check_mark)
|
||||
|
||||
@@ -153,10 +156,10 @@ async def _extctl_unload(ctx: commands.Context, extension: str):
|
||||
"""Unloads an extension."""
|
||||
try:
|
||||
bot.unload_extension(ext_dir + "." + extension)
|
||||
except commands.ExtensionNotLoaded as e:
|
||||
except discord.errors.ExtensionNotLoaded as e:
|
||||
try:
|
||||
bot.unload_extension(plugin_dir + "." + extension)
|
||||
except commands.ExtensionNotLoaded:
|
||||
except discord.errors.ExtensionNotLoaded:
|
||||
raise e
|
||||
await cmn.add_react(ctx.message, cmn.emojis.check_mark)
|
||||
|
||||
|
||||
+4
-3
@@ -1,9 +1,10 @@
|
||||
discord.py~=1.7.3
|
||||
py-cord~=2.3.2
|
||||
aiohttp[speedups]
|
||||
ctyparser~=2.0
|
||||
gridtools~=1.0
|
||||
qrztools[async]~=1.0
|
||||
callsignlookuptools[async]~=1.0
|
||||
beautifulsoup4
|
||||
pytz
|
||||
cairosvg
|
||||
requests
|
||||
httpx
|
||||
pydantic
|
||||
|
||||
@@ -34,9 +34,9 @@ while [ ! -z "$1" ]; do
|
||||
done
|
||||
|
||||
|
||||
# If $PYTHON_BIN is not defined, default to 'python3.9'
|
||||
# If $PYTHON_BIN is not defined, default to 'python3.11'
|
||||
if [ $_NO_BOTENV -eq 1 -a -z "$PYTHON_BIN" ]; then
|
||||
PYTHON_BIN='python3.9'
|
||||
PYTHON_BIN='python3.11'
|
||||
fi
|
||||
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ SPDX-License-Identifier: LiLiQ-Rplus-1.1
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import requests
|
||||
import httpx
|
||||
|
||||
from utils.resources_models import Index
|
||||
|
||||
@@ -28,8 +28,11 @@ class ResourcesManager:
|
||||
def sync_fetch(self, filepath: str):
|
||||
"""Fetches files in sync mode."""
|
||||
self.print_msg(f"Fetching {filepath}", "sync")
|
||||
with requests.get(self.url + filepath) as resp:
|
||||
return resp.content
|
||||
resp = httpx.get(self.url + filepath)
|
||||
resp.raise_for_status()
|
||||
r = resp.content
|
||||
resp.close()
|
||||
return r
|
||||
|
||||
def sync_start(self, basedir: Path) -> Index:
|
||||
"""Takes cares of constructing the local resources repository and initialising the RM."""
|
||||
@@ -40,7 +43,7 @@ class ResourcesManager:
|
||||
new_index: Index = self.parse_index(raw)
|
||||
with (basedir / "index.json").open("wb") as file:
|
||||
file.write(raw)
|
||||
except (requests.RequestException, OSError) as ex:
|
||||
except (httpx.RequestError, OSError) as ex:
|
||||
self.print_msg(f"There was an issue fetching the index: {ex.__class__.__name__}: {ex}", "sync")
|
||||
if (basedir / "index.json").exists():
|
||||
self.print_msg("Old file exist, using old resources", "fallback")
|
||||
@@ -58,7 +61,7 @@ class ResourcesManager:
|
||||
try:
|
||||
with (basedir / file.filename).open("wb") as f:
|
||||
f.write(self.sync_fetch(file.filename))
|
||||
except (requests.RequestException, OSError) as ex:
|
||||
except (httpx.RequestError, OSError) as ex:
|
||||
ex_cls = ex.__class__.__name__
|
||||
self.print_msg(f"There was an issue fetching {file.filename}: {ex_cls}: {ex}", "sync")
|
||||
if not (basedir / file.filename).exists():
|
||||
|
||||
Reference in New Issue
Block a user