Metadata update!
- Added flag emojis to commands with countries. - Added image attribution and description to "image" commands. - Added file metadata to image resources. - Added new key in options.py: pika. - Added classes to deal with file metadata. - Added common paths to common.py. - Changed directory/file names of resources. - Documented the new metadata format. Fixes #83 - Metadata storage Fixes #86 - Country flags
@ -4,6 +4,11 @@ All notable changes to this project will be documented in this file.
|
|||||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
### Added
|
||||||
|
- Flag emojis to commands with countries.
|
||||||
|
- Image attribution and description to "image" commands.
|
||||||
|
- New key in options.py: pika.
|
||||||
|
|
||||||
|
|
||||||
## [v2.0.0] - 2019-12-16
|
## [v2.0.0] - 2019-12-16
|
||||||
### Added
|
### Added
|
||||||
@ -31,7 +36,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Cleaned up code to comply with the PEP8 Standard
|
- Cleaned up code to comply with the PEP8 Standard
|
||||||
- Issue in morse and unmorse commands where spaces were not interpreted correctly
|
- Issue in morse and unmorse commands where spaces were not interpreted correctly
|
||||||
|
|
||||||
|
|
||||||
## v1.0.0 - 2019-07-31 [YANKED]
|
## v1.0.0 - 2019-07-31 [YANKED]
|
||||||
|
|
||||||
|
|
||||||
[Unreleased]: https://github.com/classabbyamp/discord-qrm2/compare/v2.0.0...HEAD
|
[Unreleased]: https://github.com/classabbyamp/discord-qrm2/compare/v2.0.0...HEAD
|
||||||
[v2.0.0]: https://github.com/classabbyamp/discord-qrm2/releases/tag/v2.0.0
|
[v2.0.0]: https://github.com/classabbyamp/discord-qrm2/releases/tag/v2.0.0
|
||||||
|
49
common.py
@ -13,17 +13,18 @@ General Public License, version 2.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
import collections
|
||||||
|
import json
|
||||||
import traceback
|
import traceback
|
||||||
|
from pathlib import Path
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import discord.ext.commands as commands
|
import discord.ext.commands as commands
|
||||||
|
|
||||||
|
|
||||||
import data.options as opt
|
import data.options as opt
|
||||||
|
|
||||||
|
|
||||||
__all__ = ["colours", "cat", "emojis", "error_embed_factory", "add_react", "check_if_owner"]
|
__all__ = ["colours", "cat", "emojis", "error_embed_factory", "add_react", "check_if_owner"]
|
||||||
|
|
||||||
|
|
||||||
@ -43,6 +44,50 @@ cat = SimpleNamespace(lookup='Information Lookup',
|
|||||||
emojis = SimpleNamespace(good='✅',
|
emojis = SimpleNamespace(good='✅',
|
||||||
bad='❌')
|
bad='❌')
|
||||||
|
|
||||||
|
paths = SimpleNamespace(data=Path("./data/"),
|
||||||
|
resources=Path("./resources/"),
|
||||||
|
bandcharts=Path("./resources/img/bandcharts/"),
|
||||||
|
maps=Path("./resources/img/maps/"))
|
||||||
|
|
||||||
|
|
||||||
|
# --- Classes ---
|
||||||
|
|
||||||
|
class ImageMetadata:
|
||||||
|
"""Represents the metadata of a single image."""
|
||||||
|
def __init__(self, metadata: list):
|
||||||
|
self.filename: str = metadata[0]
|
||||||
|
self.name: str = metadata[1]
|
||||||
|
self.long_name: str = metadata[2]
|
||||||
|
self.description: str = metadata[3]
|
||||||
|
self.source: str = metadata[4]
|
||||||
|
self.emoji: str = metadata[5]
|
||||||
|
|
||||||
|
|
||||||
|
class ImagesGroup(collections.abc.Mapping):
|
||||||
|
"""Represents a group of images, loaded from a meta.json file."""
|
||||||
|
def __init__(self, file_path):
|
||||||
|
self._images = {}
|
||||||
|
self.path = file_path
|
||||||
|
|
||||||
|
with open(file_path, "r") as file:
|
||||||
|
images: dict = json.load(file)
|
||||||
|
for key, imgdata in images.items():
|
||||||
|
self._images[key] = ImageMetadata(imgdata)
|
||||||
|
|
||||||
|
# Wrappers to implement dict-like functionality
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._images)
|
||||||
|
|
||||||
|
def __getitem__(self, key: str):
|
||||||
|
return self._images[key]
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._images)
|
||||||
|
|
||||||
|
# str(): Simply return what it would be for the underlaying dict
|
||||||
|
def __str__(self):
|
||||||
|
return str(self._images)
|
||||||
|
|
||||||
|
|
||||||
# --- Helper functions ---
|
# --- Helper functions ---
|
||||||
|
|
||||||
|
24
dev-notes/metadata-format.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# File metadata format
|
||||||
|
|
||||||
|
Used for grouping info such as name, description, source, and such.
|
||||||
|
|
||||||
|
|
||||||
|
## Format
|
||||||
|
|
||||||
|
*`id` is the dictionary key, and the list items are the attributes for each files.*
|
||||||
|
|
||||||
|
`meta.json`
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": ["filename", "name", "long_name", "description", "source", "emoji"]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
| Attribute | Description | Examples |
|
||||||
|
| ------------- | ----------------------------------------------- | ------------------------------------------------ |
|
||||||
|
| `filename` | The file name of the file, without the path. | `ca.png`, `itu.png` |
|
||||||
|
| `name` | The name of the file. | `Canada`, `ITU Zones` |
|
||||||
|
| `long_name` | The long name (title) of the file. | `Worldwide map of ITU Zones` |
|
||||||
|
| `description` | The description accompanying the file. | `Full radio allocations chart for all services.` |
|
||||||
|
| `source` | The source of the file. | `Instituto Federal de Telecomunicaciones (IFT)` |
|
||||||
|
| `emoji` | A Unicode emoji associated with the file. | `📻`, `🇨🇦` |
|
@ -9,46 +9,78 @@ General Public License, version 2.
|
|||||||
|
|
||||||
import io
|
import io
|
||||||
|
|
||||||
|
import aiohttp
|
||||||
|
|
||||||
import discord
|
import discord
|
||||||
import discord.ext.commands as commands
|
import discord.ext.commands as commands
|
||||||
|
|
||||||
import aiohttp
|
|
||||||
|
|
||||||
import common as cmn
|
import common as cmn
|
||||||
|
|
||||||
|
|
||||||
class ImageCog(commands.Cog):
|
class ImageCog(commands.Cog):
|
||||||
def __init__(self, bot: commands.Bot):
|
def __init__(self, bot: commands.Bot):
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
|
self.bandcharts = cmn.ImagesGroup(cmn.paths.bandcharts / "meta.json")
|
||||||
|
self.maps = cmn.ImagesGroup(cmn.paths.maps / "meta.json")
|
||||||
|
|
||||||
@commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref)
|
@commands.command(name="bandplan", aliases=['plan', 'bands'], category=cmn.cat.ref)
|
||||||
async def _bandplan(self, ctx: commands.Context, region: str = ''):
|
async def _bandplan(self, ctx: commands.Context, region: str = ''):
|
||||||
'''Posts an image of Frequency Allocations.'''
|
'''Posts an image of Frequency Allocations.'''
|
||||||
name = {'cn': 'Chinese',
|
|
||||||
'ca': 'Canadian',
|
|
||||||
'nl': 'Dutch',
|
|
||||||
'us': 'US',
|
|
||||||
'mx': 'Mexican'}
|
|
||||||
arg = region.lower()
|
arg = region.lower()
|
||||||
|
|
||||||
with ctx.typing():
|
with ctx.typing():
|
||||||
embed = cmn.embed_factory(ctx)
|
embed = cmn.embed_factory(ctx)
|
||||||
if arg not in name:
|
if arg not in self.bandcharts:
|
||||||
desc = 'Possible arguments are:\n'
|
desc = 'Possible arguments are:\n'
|
||||||
for abbrev, title in name.items():
|
for key, img in self.bandcharts.items():
|
||||||
desc += f'`{abbrev}`: {title}\n'
|
desc += f'`{key}`: {img.name}{(" " + img.emoji if img.emoji else "")}\n'
|
||||||
embed.title = f'Bandplan Not Found!'
|
embed.title = f'Bandplan Not Found!'
|
||||||
embed.description = desc
|
embed.description = desc
|
||||||
embed.colour = cmn.colours.bad
|
embed.colour = cmn.colours.bad
|
||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
else:
|
else:
|
||||||
img = discord.File(f"resources/images/bandchart/{arg}bandchart.png",
|
metadata: cmn.ImageMetadata = self.bandcharts[arg]
|
||||||
filename=f'{arg}bandchart.png')
|
img = discord.File(cmn.paths.bandcharts / metadata.filename,
|
||||||
embed.title = f'{name[arg]} Amateur Radio Bands'
|
filename=metadata.filename)
|
||||||
|
if metadata.description:
|
||||||
|
embed.description = metadata.description
|
||||||
|
if metadata.source:
|
||||||
|
embed.add_field(name="Source", value=metadata.source)
|
||||||
|
embed.title = metadata.long_name + (" " + metadata.emoji if metadata.emoji else "")
|
||||||
embed.colour = cmn.colours.good
|
embed.colour = cmn.colours.good
|
||||||
embed.set_image(url=f'attachment://{arg}bandchart.png')
|
embed.set_image(url='attachment://' + metadata.filename)
|
||||||
await ctx.send(embed=embed, file=img)
|
await ctx.send(embed=embed, file=img)
|
||||||
|
|
||||||
|
@commands.command(name="map", category=cmn.cat.maps)
|
||||||
|
async def _map(self, ctx: commands.Context, map_id: str = ''):
|
||||||
|
'''Posts an image of a ham-relevant map.'''
|
||||||
|
arg = map_id.lower()
|
||||||
|
|
||||||
|
with ctx.typing():
|
||||||
|
embed = cmn.embed_factory(ctx)
|
||||||
|
if arg not in self.maps:
|
||||||
|
desc = 'Possible arguments are:\n'
|
||||||
|
for key, img in self.maps.items():
|
||||||
|
desc += f'`{key}`: {img.name}{(" " + img.emoji if img.emoji else "")}\n'
|
||||||
|
embed.title = 'Map Not Found!'
|
||||||
|
embed.description = desc
|
||||||
|
embed.colour = cmn.colours.bad
|
||||||
|
await ctx.send(embed=embed)
|
||||||
|
else:
|
||||||
|
metadata: cmn.ImageMetadata = self.maps[arg]
|
||||||
|
img = discord.File(cmn.paths.maps / metadata.filename,
|
||||||
|
filename=metadata.filename)
|
||||||
|
if metadata.description:
|
||||||
|
embed.description = metadata.description
|
||||||
|
if metadata.source:
|
||||||
|
embed.add_field(name="Source", value=metadata.source)
|
||||||
|
embed.title = metadata.long_name + (" " + metadata.emoji if metadata.emoji else "")
|
||||||
|
embed.colour = cmn.colours.good
|
||||||
|
embed.set_image(url='attachment://' + metadata.filename)
|
||||||
|
await ctx.send(embed=embed, file=img)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl'], category=cmn.cat.maps)
|
@commands.command(name="grayline", aliases=['greyline', 'grey', 'gray', 'gl'], category=cmn.cat.maps)
|
||||||
async def _grayline(self, ctx: commands.Context):
|
async def _grayline(self, ctx: commands.Context):
|
||||||
'''Posts a map of the current greyline, where HF propagation is the best.'''
|
'''Posts a map of the current greyline, where HF propagation is the best.'''
|
||||||
@ -68,35 +100,6 @@ class ImageCog(commands.Cog):
|
|||||||
embed.set_image(url=f'attachment://greyline.jpg')
|
embed.set_image(url=f'attachment://greyline.jpg')
|
||||||
await ctx.send(embed=embed, file=discord.File(data, 'greyline.jpg'))
|
await ctx.send(embed=embed, file=discord.File(data, 'greyline.jpg'))
|
||||||
|
|
||||||
@commands.command(name="map", category=cmn.cat.maps)
|
|
||||||
async def _map(self, ctx: commands.Context, map_id: str = ''):
|
|
||||||
'''Posts an image of a ham-relevant map.'''
|
|
||||||
map_titles = {"cq": 'Worldwide CQ Zones Map',
|
|
||||||
"itu": 'Worldwide ITU Zones Map',
|
|
||||||
"arrl": 'ARRL/RAC Section Map',
|
|
||||||
"rac": 'ARRL/RAC Section Map',
|
|
||||||
"cn": 'Chinese Callsign Areas',
|
|
||||||
"us": 'US Callsign Areas'}
|
|
||||||
|
|
||||||
arg = map_id.lower()
|
|
||||||
with ctx.typing():
|
|
||||||
embed = cmn.embed_factory(ctx)
|
|
||||||
if arg not in map_titles:
|
|
||||||
desc = 'Possible arguments are:\n'
|
|
||||||
for abbrev, title in map_titles.items():
|
|
||||||
desc += f'`{abbrev}`: {title}\n'
|
|
||||||
embed.title = 'Map Not Found!'
|
|
||||||
embed.description = desc
|
|
||||||
embed.colour = cmn.colours.bad
|
|
||||||
await ctx.send(embed=embed)
|
|
||||||
else:
|
|
||||||
img = discord.File(f"resources/images/map/{arg}map.png",
|
|
||||||
filename=f'{arg}map.png')
|
|
||||||
embed.title = f'{map_titles[arg]}'
|
|
||||||
embed.colour = cmn.colours.good
|
|
||||||
embed.set_image(url=f'attachment://{arg}map.png')
|
|
||||||
await ctx.send(embed=embed, file=img)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot: commands.Bot):
|
def setup(bot: commands.Bot):
|
||||||
bot.add_cog(ImageCog(bot))
|
bot.add_cog(ImageCog(bot))
|
||||||
|
6
main.py
@ -87,8 +87,12 @@ async def _extctl_load(ctx: commands.Context, extension: str):
|
|||||||
await ctx.send(embed=embed)
|
await ctx.send(embed=embed)
|
||||||
|
|
||||||
|
|
||||||
@_extctl.command(name="reload")
|
@_extctl.command(name="reload", aliases=["relaod"])
|
||||||
async def _extctl_reload(ctx: commands.Context, extension: str):
|
async def _extctl_reload(ctx: commands.Context, extension: str):
|
||||||
|
if ctx.invoked_with == "relaod":
|
||||||
|
pika = bot.get_emoji(opt.pika)
|
||||||
|
if pika:
|
||||||
|
await cmn.add_react(ctx.message, pika)
|
||||||
try:
|
try:
|
||||||
bot.reload_extension(ext_dir + "." + extension)
|
bot.reload_extension(ext_dir + "." + extension)
|
||||||
await cmn.add_react(ctx.message, cmn.emojis.good)
|
await cmn.add_react(ctx.message, cmn.emojis.good)
|
||||||
|
Before Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 72 KiB |
Before Width: | Height: | Size: 153 KiB After Width: | Height: | Size: 153 KiB |
7
resources/img/bandcharts/meta.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"ca": ["ca.png", "Canada", "Amateur radio bands in Canada", "**This bandplan is incomplete**; some bands, like 630m, are simply not present. It also does not cover any band above 30MHz.", "[RAC 0-30MHz Band Plan](https://www.rac.ca/wp-content/uploads/files/pdf/RAC%20Bandplan%20December%201%202015.pdf)", "🇨🇦"],
|
||||||
|
"cn": ["cn.png", "China", "Amateur radio bands in China", "", "Created by KN8U and NY7H", "🇨🇳"],
|
||||||
|
"mx": ["mx.png", "Mexico", "Radio allocations in Mexico", "Full radio allocations chart for all services. No information specific to amateur radio is shown.", "Secretaría de Comunicaciones y Transportes (SCT) / Instituto Federal de Telecomunicaciones (IFT)", "🇲🇽"],
|
||||||
|
"nl": ["nl.png", "Netherlands", "Amateur radio bands in the Netherlands", "", "", "🇳🇱"],
|
||||||
|
"us": ["us.png", "USA", "Amateur radio bands in the USA", "", "*[ARRL Frequency Chart](https://www.arrl.org/shop/ARRL-Frequency-Chart-11-17/)* [[PDF]](http://www.arrl.org/files/file/Regulatory/Band%20Chart/Band%20Chart%20-%2011X17%20Color.pdf)", "🇺🇸"]
|
||||||
|
}
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
Before Width: | Height: | Size: 160 KiB After Width: | Height: | Size: 160 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 84 KiB |
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 504 KiB After Width: | Height: | Size: 504 KiB |
6
resources/img/maps/meta.json
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"arrl": ["arrl-rac.png", "ARRL Sections", "ARRL Sections", "", "", "🇺🇸"],
|
||||||
|
"rac": ["arrl-rac.png", "RAC Sections", "RAC Sections", "", "", "🇨🇦"],
|
||||||
|
"cn": ["cn.png", "China's Prefixes", "Map of prefix regions in China", "", "CRAC", "🇨🇳"],
|
||||||
|
"us": ["us.png", "USA's Prefixes", "Map of prefix regions in the USA", "", "*[ARRL WAS Map](https://www.arrl.org/was-forms)* [[PDF]](http://www.arrl.org/files/file/Awards%20Application%20Forms/WASmap_Color.pdf)", "🇺🇸"]
|
||||||
|
}
|
Before Width: | Height: | Size: 136 KiB After Width: | Height: | Size: 136 KiB |
@ -31,3 +31,6 @@ exts = ['ae7q', 'base', 'fun', 'grid', 'ham', 'image', 'lookup', 'morse', 'qrz',
|
|||||||
|
|
||||||
# The text to put in the "playing" status.
|
# The text to put in the "playing" status.
|
||||||
game = 'with lids on 7.200'
|
game = 'with lids on 7.200'
|
||||||
|
|
||||||
|
# A :pika: emote's ID, None for no emote :c
|
||||||
|
pika = 658733876176355338
|
||||||
|