Skip to content

Commit 6ceeebb

Browse files
committed
added stats bot
1 parent cca131b commit 6ceeebb

File tree

4 files changed

+218
-4
lines changed

4 files changed

+218
-4
lines changed

tools/statistics_app/.env.example

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
# DISCORD BOT TOKEN
2-
DISCORD_TOKEN=TOKEN
3-
# DISCORD BOT ID
4-
CLIENT_ID=ID
1+
# Discord Bot Configuration
2+
GUILD_ID=YOUR_GUILD_ID_HERE
3+
BOT_TOKEN=YOUR_BOT_TOKEN_HERE
4+
APP_ID=YOUR_APPLICATION_ID_HERE
5+
CATEGORY_ID=YOUR_CATEGORY_ID_HERE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Updated": 1354131911940767838,
3+
"Total Cases": 1354131913534734356,
4+
"Active Cases": 1354131914713333915,
5+
"Inactive Cases": 1354131915875156120,
6+
"Total Servers": 1354131916865015839,
7+
"Total Members": 1354131918421233746
8+
}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"PROJECT_CW": "https://discord.gg/projectcw",
3+
"BLITZHUB_EU": "https://discord.gg/WKz7RJvtUK",
4+
"SINEWAVE_DEVELOPMENT": "https://discord.gg/2nHHHBWNDw",
5+
"BLITZPOST": "https://discord.com/invite/q82WM4E"
6+
}

tools/statistics_app/main.py

+199
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
import os
2+
import json
3+
import asyncio
4+
import aiohttp
5+
from datetime import datetime, timezone
6+
from dotenv import load_dotenv
7+
import discord
8+
from discord.ext import commands, tasks
9+
import logging
10+
11+
# Setup logging
12+
logging.basicConfig(level=logging.INFO,
13+
format='[%(asctime)s] [%(levelname)-8s] %(message)s',
14+
datefmt='%Y-%m-%d %H:%M:%S')
15+
logger = logging.getLogger(__name__)
16+
17+
# Load environment variables
18+
load_dotenv()
19+
20+
# Environment variables
21+
GUILD_ID = int(os.getenv('GUILD_ID'))
22+
BOT_TOKEN = os.getenv('BOT_TOKEN')
23+
APP_ID = os.getenv('APP_ID')
24+
CATEGORY_ID = int(os.getenv('CATEGORY_ID'))
25+
JSON_URL = 'https://raw.githubusercontent.com/ThatSINEWAVE/CDA-Project/main/data/Compromised-Discord-Accounts.json'
26+
27+
# Config file paths
28+
CONFIG_FOLDER = 'config'
29+
INVITES_CONFIG_PATH = os.path.join(CONFIG_FOLDER, 'invites.json')
30+
CHANNELS_CONFIG_PATH = os.path.join(CONFIG_FOLDER, 'channels.json')
31+
32+
# Intents setup
33+
intents = discord.Intents.default()
34+
intents.message_content = True
35+
intents.guilds = True
36+
bot = commands.Bot(command_prefix='!', intents=intents)
37+
38+
# Channel names and order
39+
CHANNEL_NAMES = [
40+
'Updated',
41+
'Total Cases',
42+
'Active Cases',
43+
'Inactive Cases',
44+
'Total Servers',
45+
'Total Members'
46+
]
47+
48+
49+
async def fetch_json_data():
50+
"""Fetch JSON data from the GitHub URL with error handling"""
51+
try:
52+
async with aiohttp.ClientSession() as session:
53+
async with session.get(JSON_URL) as response:
54+
# Try to parse the response
55+
try:
56+
data = json.loads(await response.text())
57+
return data
58+
except json.JSONDecodeError as e:
59+
logger.error(f"JSON Decode Error: {e}")
60+
return None
61+
except Exception as e:
62+
logger.error(f"Error fetching JSON: {e}")
63+
return None
64+
65+
66+
def load_config_file(filepath):
67+
"""Load JSON configuration file with error handling"""
68+
try:
69+
with open(filepath, 'r') as f:
70+
return json.load(f)
71+
except FileNotFoundError:
72+
logger.error(f"Config file not found at {filepath}")
73+
return {}
74+
except json.JSONDecodeError:
75+
logger.error(f"Error parsing config file at {filepath}")
76+
return {}
77+
78+
79+
async def get_total_members(invites):
80+
"""Get total member count across all servers"""
81+
total_members = 0
82+
83+
for server_name, invite_code in invites.items():
84+
try:
85+
# Attempt to get invite using the invite code
86+
invite = await bot.fetch_invite(invite_code)
87+
total_members += invite.approximate_member_count or 0
88+
logger.info(f"Server {server_name}: {invite.approximate_member_count} members")
89+
except discord.NotFound:
90+
logger.warning(f"Invite for {server_name} is invalid or expired")
91+
except Exception as e:
92+
logger.error(f"Error fetching invite for {server_name}: {e}")
93+
94+
return total_members
95+
96+
97+
def analyze_data(data):
98+
"""Analyze the JSON data and return statistics"""
99+
if not data:
100+
logger.warning("No data to analyze")
101+
return {
102+
'last_update': datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M'),
103+
'total_cases': 0,
104+
'active_cases': 0,
105+
'inactive_cases': 0,
106+
'total_servers': 0,
107+
'total_members': 0
108+
}
109+
110+
# Get the latest timestamp
111+
latest_timestamp = datetime.now(timezone.utc).strftime('%Y-%m-%d %H:%M')
112+
113+
# Calculate statistics
114+
total_cases = len(data)
115+
active_cases = sum(1 for entry in data.values() if entry.get('FINAL_URL_STATUS') == 'ACTIVE')
116+
inactive_cases = total_cases - active_cases
117+
total_servers = len(set(entry.get('FOUND_ON_SERVER', '') for entry in data.values()))
118+
119+
return {
120+
'last_update': latest_timestamp,
121+
'total_cases': total_cases,
122+
'active_cases': active_cases,
123+
'inactive_cases': inactive_cases,
124+
'total_servers': total_servers,
125+
'total_members': 0 # Will be updated later
126+
}
127+
128+
129+
@tasks.loop(minutes=10)
130+
async def update_channels():
131+
"""Periodically update channels with project statistics"""
132+
try:
133+
# Load channel configuration
134+
channel_config = load_config_file(CHANNELS_CONFIG_PATH)
135+
136+
# Fetch guild
137+
guild = bot.get_guild(GUILD_ID)
138+
139+
if not guild:
140+
logger.error(f"Guild with ID {GUILD_ID} not found.")
141+
return
142+
143+
# Fetch and analyze data
144+
data = await fetch_json_data()
145+
146+
if data is None:
147+
logger.error("Failed to fetch or parse JSON data")
148+
return
149+
150+
# Analyze data
151+
stats = analyze_data(data)
152+
153+
# Load and calculate total members
154+
invites = load_config_file(INVITES_CONFIG_PATH)
155+
stats['total_members'] = await get_total_members(invites)
156+
157+
# Update channel names
158+
channel_stats = [
159+
stats['last_update'],
160+
str(stats['total_cases']),
161+
str(stats['active_cases']),
162+
str(stats['inactive_cases']),
163+
str(stats['total_servers']),
164+
str(stats['total_members'])
165+
]
166+
167+
# Update channels based on configuration
168+
for i, (name, stat) in enumerate(zip(CHANNEL_NAMES, channel_stats)):
169+
channel_id = channel_config.get(name)
170+
if channel_id:
171+
try:
172+
channel = guild.get_channel(int(channel_id))
173+
if channel:
174+
await channel.edit(name=str(stat))
175+
logger.info(f"Updated {name} channel to: {stat}")
176+
else:
177+
logger.warning(f"Channel {name} with ID {channel_id} not found")
178+
except Exception as e:
179+
logger.error(f"Error updating channel {name}: {e}")
180+
181+
logger.info("Channels updated successfully.")
182+
183+
except Exception as e:
184+
logger.error(f"Error updating channels: {e}")
185+
186+
187+
@bot.event
188+
async def on_ready():
189+
"""Bot startup event"""
190+
logger.info(f'Logged in as {bot.user.name}')
191+
try:
192+
await bot.tree.sync()
193+
update_channels.start()
194+
except Exception as e:
195+
logger.error(f"Error syncing commands or starting tasks: {e}")
196+
197+
198+
# Run the bot
199+
bot.run(BOT_TOKEN)

0 commit comments

Comments
 (0)