Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
424e3fe
Update check-cogs.yml
BenCos17 Nov 20, 2025
43b78ca
Update check-cogs.yml
BenCos17 Nov 20, 2025
65242e3
using feature branch to test automatic installing requirements
NNTin Nov 20, 2025
f14e653
missing requirements in cog
NNTin Nov 20, 2025
ad08d10
add yt_dlp dependency
NNTin Nov 20, 2025
bf90418
missing reportlab dependency
NNTin Nov 20, 2025
f814200
fix relative import
NNTin Nov 20, 2025
4cf37d9
wtforms missing
NNTin Nov 20, 2025
bfd62c3
new v1 release with the update of automatic requirements installation
NNTin Nov 20, 2025
21be621
Merge pull request #7 from NNTin/feature/update-dflows
BenCos17 Nov 21, 2025
ca52849
stuff
BenCos17 Nov 28, 2025
d45486e
Revert "stuff"
BenCos17 Nov 28, 2025
3e6dc59
custom watchlist wip
BenCos17 Dec 5, 2025
4875399
Update skysearch.py
BenCos17 Dec 5, 2025
433deb9
Update aircraft.py
BenCos17 Dec 5, 2025
4536caa
docs
BenCos17 Dec 5, 2025
2ad5a1e
watchlist cooldown adjustment and update docs
BenCos17 Dec 5, 2025
e52b6b8
fix conflict
BenCos17 Dec 5, 2025
c02dcd8
tweaks
BenCos17 Dec 5, 2025
7cdf2b5
take off/landing
BenCos17 Dec 5, 2025
a7386a8
fix mistake
BenCos17 Dec 5, 2025
4052ae7
Update helpers.py
BenCos17 Dec 5, 2025
2743943
Update info.json
BenCos17 Dec 8, 2025
00a242f
Update README.md
BenCos17 Dec 9, 2025
bc05a00
Update README.md
BenCos17 Dec 9, 2025
844a712
skysearch.skysearch.on_message work
BenCos17 Dec 12, 2025
bd591f2
Update skysearch.py
BenCos17 Dec 12, 2025
2bf6022
Update skysearch.py
BenCos17 Dec 12, 2025
b2925ec
Update skysearch.py
BenCos17 Dec 12, 2025
dc1b028
translation stuff
BenCos17 Dec 12, 2025
f6c0d8a
Add Counter and Voice cogs with documentation
BenCos17 Jan 2, 2026
709b47d
fix copypaste id mistake
BenCos17 Jan 2, 2026
b2029a0
Update counter.py
BenCos17 Jan 2, 2026
f1b3858
Add multi-counter support and owner requests for guild counters
BenCos17 Jan 2, 2026
cf032a0
repo cleanup
BenCos17 Jan 5, 2026
9929d2e
fix broken author key
BenCos17 Jan 5, 2026
016227f
shard info cog
BenCos17 Jan 6, 2026
0abab78
Update clusters.py
BenCos17 Jan 6, 2026
c45aba5
Update clusters.py
BenCos17 Jan 6, 2026
1456e32
Update clusters.py
BenCos17 Jan 6, 2026
e5b3e9f
Update clusters.py
BenCos17 Jan 6, 2026
f44ac67
fix uptime I broke
BenCos17 Jan 6, 2026
fcd465a
Update clusters.py
BenCos17 Jan 6, 2026
15528c9
Create docs.md
BenCos17 Jan 6, 2026
aea2d28
Update docs.md
BenCos17 Jan 6, 2026
a659509
Update docs.md
BenCos17 Jan 6, 2026
9417ce3
Update docs.md
BenCos17 Jan 6, 2026
1ed577e
Update docs.md
BenCos17 Jan 6, 2026
db80e70
Update docs.md
BenCos17 Jan 6, 2026
34d54ca
Update docs.md
BenCos17 Jan 6, 2026
12ea0e6
Update docs.md
BenCos17 Jan 6, 2026
36f9756
Update docs.md
BenCos17 Jan 6, 2026
6140af5
Update docs.md
BenCos17 Jan 6, 2026
19d2d9c
Update docs.md
BenCos17 Jan 6, 2026
cfe5418
Update docs.md
BenCos17 Jan 6, 2026
caa8f95
Update docs.md
BenCos17 Jan 6, 2026
fc99e05
.
BenCos17 Jan 8, 2026
09540b9
fix it accidently ending up in .github 🤦‍♂️
BenCos17 Jan 8, 2026
4565bfd
how was it blank when I moved it....wth
BenCos17 Jan 8, 2026
5b5995a
Update martineimages.py
BenCos17 Jan 8, 2026
4d70fbc
Update martineimages.py
BenCos17 Jan 8, 2026
6f0f139
Update martineimages.py
BenCos17 Jan 8, 2026
fdafd85
Update martineimages.py
BenCos17 Jan 8, 2026
a0c9520
hmm
BenCos17 Jan 14, 2026
4f5b935
fix aircraft feeder command private submit system not working
BenCos17 Jan 14, 2026
2985cc8
Merge branch 'main' of https://github.com/BenCos17/ben-cogs
BenCos17 Jan 14, 2026
4797eda
Update emojilink.py
BenCos17 Jan 14, 2026
451f465
add a way to set the user agent
BenCos17 Jan 20, 2026
a18e65c
Update README.md
BenCos17 Jan 20, 2026
38aaadd
Add radiosonde cog for SondeHub tracking
BenCos17 Feb 5, 2026
ae31c6b
fix
BenCos17 Feb 5, 2026
68c64c3
stuff
BenCos17 Feb 5, 2026
1424235
more
BenCos17 Feb 5, 2026
dd352a7
vcvc
BenCos17 Feb 5, 2026
ebbc2c0
radiosonde sites system
BenCos17 Feb 5, 2026
5aa8b78
tweaks to sites command to allow names
BenCos17 Feb 5, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions .github/workflows/check-cogs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:

env:
BUILD_ARTIFACT_NAME: "my-build-artifact"
COG_PATHS: "dworld" # comma-separated list of cog folder names
COG_PATHS: "ampremover,bell,bible,currency,dank,earthquake,emojilink,enumbers,example,facebookdownloader,imgen,invoice,loan,scp,seasons,servertools,skysearch,spamatron,talknotifier,xkcd" # comma-separated list of cog folder names
RPC_PORT: "6133"

jobs:
Expand Down Expand Up @@ -49,11 +49,6 @@ jobs:
optional_args: "--no-cogs" # Example optional argument to run without loading any cogs
# --dry-run fails due to bug in Red-DiscordBot https://github.com/Cog-Creators/Red-DiscordBot/issues/6572
continue-on-error: true

# For dworld cog d-back and wtforms are required
- name: Install dependencies
run: |
uv pip install d-back wtforms --system

# Actual magic: test that cogs can be loaded/unloaded via RPC
- name: Test cogs via RPC
Expand Down Expand Up @@ -168,4 +163,4 @@ jobs:
description: >-
${{ needs.build-validation-fields.outputs.validation_result == 'success' && format('All cogs ({0}) passed build and RPC validation.', env.COG_PATHS) || format('One or more stages failed for cogs: {0}. Review the workflow logs.', env.COG_PATHS) }}
color: ${{ needs.build-validation-fields.outputs.validation_result == 'success' && '3066993' || '16776960' }}
fields: ${{ needs.build-validation-fields.outputs.discord_fields }}
fields: ${{ needs.build-validation-fields.outputs.discord_fields }}
8 changes: 4 additions & 4 deletions imgen/info.json → bible/info.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
"author": ["bencos18 (492089091320446976)"],
"install_msg": "thank for adding my repo\n feel free to message me on discord or create a github issue if you need support or help with anything.",
"short": "some random cogs I made for my own use and decided to make public.",
"description": "Imgen cog.",
"tags": ["emoji", "emojis"],
"requirements": ["bible"],
"description": "-",
"tags": [],
"end_user_data_statement": "This cog does not store any End User Data.",
"type": "COG",
"hidden" : true
"type": "COG"
}
4 changes: 4 additions & 0 deletions clusters/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .clusters import Clusters

async def setup(bot):
await bot.add_cog(Clusters(bot))
157 changes: 157 additions & 0 deletions clusters/clusters.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import discord
from redbot.core import commands, Config
import psutil, datetime, json, aiohttp
from aiohttp import web

MARVEL_NAMES = [
"IronMan", "Thor", "Hulk", "BlackWidow", "CaptainAmerica", "Loki",
"DoctorStrange", "SpiderMan", "BlackPanther", "ScarletWitch"
]

class Clusters(commands.Cog):
"""Shows dynamic Marvel-themed cluster status with customizable names and uptime, plus a web endpoint."""

def __init__(self, bot):
self.bot = bot
self.config = Config.get_conf(self, identifier=1234567890)
self.config.register_global(custom_names={})
self.shard_names = {}

# Start aiohttp web server
self.app = web.Application()
self.app.add_routes([web.get('/clusters', self.web_clusters)])
self.runner = web.AppRunner(self.app)
self.bot.loop.create_task(self.start_webserver())

async def start_webserver(self):
await self.runner.setup()
self.site = web.TCPSite(self.runner, '0.0.0.0', 8080) # Change IP/port if needed
await self.site.start()

async def initialize_shard_names(self):
"""Load names from config or assign defaults based on shard ID."""
custom_names = await self.config.custom_names()
for shard_id in self.bot.shards.keys():
if str(shard_id) in custom_names:
self.shard_names[shard_id] = custom_names[str(shard_id)]
else:
self.shard_names[shard_id] = MARVEL_NAMES[shard_id % len(MARVEL_NAMES)]

def format_timedelta(self, td: datetime.timedelta):
"""Format a timedelta into weeks, days, hours."""
total_seconds = int(td.total_seconds())
weeks, remainder = divmod(total_seconds, 604800)
days, remainder = divmod(remainder, 86400)
hours, _ = divmod(remainder, 3600)
return f"{weeks} weeks and {days} days and {hours} hours ago"

def get_server_uptime(self):
"""Return server uptime as timedelta."""
boot_timestamp = psutil.boot_time()
return datetime.datetime.utcnow() - datetime.datetime.utcfromtimestamp(boot_timestamp)

@commands.command()
async def clusters(self, ctx):
"""Shows the status of all clusters using an embed."""
await self.initialize_shard_names()

# Bot uptime (Red tracks this as bot.uptime)
bot_start_time = getattr(self.bot, "uptime", None)
if bot_start_time is None:
bot_uptime_str = "Unknown"
else:
if isinstance(bot_start_time, datetime.datetime):
td = datetime.datetime.utcnow() - bot_start_time
else:
td = bot_start_time
bot_uptime_str = self.format_timedelta(td)

server_uptime = self.format_timedelta(self.get_server_uptime())

embed = discord.Embed(
title="Cluster Status",
description=f"**Bot uptime:** {bot_uptime_str}\n**Server uptime:** {server_uptime}",
color=discord.Color.blue()
)

for shard_id, name in self.shard_names.items():
cpu = psutil.cpu_percent(interval=None)
ram = psutil.virtual_memory().used / 1024**3
latency = round(self.bot.shards[shard_id].latency * 1000)

guilds = [g for g in self.bot.guilds if g.shard_id == shard_id]
servers = len(guilds)
users = sum(g.member_count or 0 for g in guilds)

value = (
f"**Status:** Alive Running\n"
f"**CPU:** {cpu:.1f}%\n"
f"**RAM:** {ram:.1f} GiB\n"
f"**Latency:** {latency} ms\n"
f"**Servers:** {servers}\n"
f"**Users:** {users}\n"
f"**Shards:** [{shard_id}]"
)

embed.add_field(name=f"Cluster #{name}", value=value, inline=False)

await ctx.send(embed=embed)

@commands.is_owner()
@commands.command()
async def renamecluster(self, ctx, shard_id: int, *, new_name: str):
"""Rename a cluster persistently. Owner only."""
if shard_id not in self.bot.shards:
await ctx.send(f"Shard ID {shard_id} does not exist.")
return

custom_names = await self.config.custom_names()
custom_names[str(shard_id)] = new_name
await self.config.custom_names.set(custom_names)
self.shard_names[shard_id] = new_name

await ctx.send(f"Cluster {shard_id} has been renamed to **{new_name}**.")

async def web_clusters(self, request):
"""Return cluster data as JSON for web endpoint."""
await self.initialize_shard_names()

# Bot uptime
bot_start_time = getattr(self.bot, "uptime", None)
if bot_start_time is None:
bot_uptime_str = "Unknown"
else:
if isinstance(bot_start_time, datetime.datetime):
td = datetime.datetime.utcnow() - bot_start_time
else:
td = bot_start_time
bot_uptime_str = self.format_timedelta(td)

server_uptime_str = self.format_timedelta(self.get_server_uptime())

data = {
"bot_uptime": bot_uptime_str,
"server_uptime": server_uptime_str,
"clusters": []
}

for shard_id, name in self.shard_names.items():
guilds = [g for g in self.bot.guilds if g.shard_id == shard_id]
servers = len(guilds)
users = sum(g.member_count or 0 for g in guilds)
latency = round(self.bot.shards[shard_id].latency * 1000)
cpu = psutil.cpu_percent(interval=None)
ram = psutil.virtual_memory().used / 1024**3

cluster_data = {
"shard_id": shard_id,
"name": name,
"servers": servers,
"users": users,
"latency_ms": latency,
"cpu_percent": cpu,
"ram_gib": ram
}
data["clusters"].append(cluster_data)

return web.Response(text=json.dumps(data, indent=2), content_type="application/json")
56 changes: 56 additions & 0 deletions clusters/docs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# How to Use Clusters


## Getting Started



### First Time Setup

1\. **Load the cog** in your Red-DiscordBot instance

[p]repo add ben-cogs https://github.com/bencos17/ben-cogs

[p]cog install ben-cogs clusters

### Basic Commands

- `[p]clusters` - main cluster info command
- `[p]renamecluster ` - allows the bot owner to override the default cluster names



## Usage

\[p]clusters
prints out the bots current clusters

for example this is my current output on my bot

<img width="506" height="516" alt="image" src="https://github.com/user-attachments/assets/f44dd0d7-df98-410b-8b23-8a66f3d9cc7f" />







[p]renamecluster <shard\_id> <new\_name>





<shard\_id> is a number between 0 and your mac amount of shards

<new\_name> what you want the cluter to be called from now on

this is how it's used

<img width="391" height="120" alt="image" src="https://github.com/user-attachments/assets/43378549-920d-4443-8ea1-fb2ccd6430e0" />






7 changes: 7 additions & 0 deletions clusters/info.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"name": "Clusters",
"author": "bencos17",
"description": "Shows dynamic Marvel-themed cluster statuses for your bot.",
"requirements": [],
"tags": ["clusters", "shards", "status"]
}
14 changes: 14 additions & 0 deletions counter/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""Counter cog package for ben-cogs

Adds a simple, flexible counter system that supports per-guild, per-user, and global counters.
"""

from .counter import Counter

__red_end_user_data_statement__ = (
"This cog stores counters and their numeric values. It stores guild IDs, user IDs (for user-scoped counters), "
"counter names, and numeric values. It does not store message content or other personal data."
)

async def setup(bot):
await bot.add_cog(Counter(bot))
Loading
Loading