nroggendorff commited on
Commit
48b6be9
1 Parent(s): 0e1f52c

add tester

Browse files

no clue if this works, testing is hard without the same env

.venv/Lib/site-packages/charset_normalizer/md.cp311-win_amd64.pyd ADDED
Binary file (10.8 kB). View file
 
.venv/Lib/site-packages/charset_normalizer/md__mypyc.cp311-win_amd64.pyd ADDED
Binary file (119 kB). View file
 
.venv/Lib/site-packages/psutil/_psutil_windows.pyd ADDED
Binary file (67.1 kB). View file
 
.venv/Lib/site-packages/pyzmq.libs/msvcp140-587ef1e98d22156d27ef55ba57e44cb3.dll ADDED
Binary file (622 kB). View file
 
.venv/Lib/site-packages/tornado/speedups.pyd ADDED
Binary file (10.8 kB). View file
 
.venv/Lib/site-packages/yaml/_yaml.cp311-win_amd64.pyd ADDED
Binary file (234 kB). View file
 
.venv/Lib/site-packages/zmq/backend/cython/_zmq.cp311-win_amd64.pyd ADDED
Binary file (823 kB). View file
 
.venv/Scripts/python.exe ADDED
Binary file (271 kB). View file
 
README.md CHANGED
@@ -1,12 +1,12 @@
1
- ---
2
- title: LevelBot
3
- emoji: 🐠
4
- colorFrom: green
5
- colorTo: blue
6
- sdk: gradio
7
- sdk_version: 4.9.0
8
- app_file: app.py
9
- pinned: false
10
- ---
11
-
12
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
+ ---
2
+ title: LevelBot
3
+ emoji: 🐠
4
+ colorFrom: green
5
+ colorTo: blue
6
+ sdk: gradio
7
+ sdk_version: 4.9.0
8
+ app_file: startup.py
9
+ pinned: false
10
+ ---
11
+
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
__init__.py ADDED
File without changes
app.py CHANGED
@@ -1,780 +1,780 @@
1
- import asyncio
2
- import csv
3
- import datetime
4
- import json
5
- import logging
6
- import os
7
- import os.path
8
- import random
9
- import re
10
- import threading
11
- import time
12
-
13
- import discord
14
- import gradio as gr
15
- import gradio_client
16
- import gspread
17
- import numpy as np
18
- import pandas as pd
19
- import requests
20
-
21
- from requests import HTTPError
22
- from apscheduler.executors.pool import ThreadPoolExecutor
23
- from apscheduler.schedulers.background import BackgroundScheduler
24
- from discord import Color, Embed
25
- from discord.ext import commands, tasks
26
- from gradio_client import Client
27
- from gspread_dataframe import get_as_dataframe, set_with_dataframe
28
- from gspread_formatting.dataframe import format_with_dataframe
29
- from huggingface_hub import HfApi, list_liked_repos, list_metrics, list_models
30
- from tabulate import tabulate
31
- from datetime import datetime
32
-
33
-
34
- DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None)
35
- intents = discord.Intents.all()
36
- bot = commands.Bot(command_prefix='!', intents=intents)
37
-
38
-
39
- """"""
40
- XP_PER_MESSAGE = 10 # 100k messages = 1M exp = lvl 100
41
- """"""
42
- service_account = json.loads(os.environ.get('KEY'))
43
- file_path = 'service_account.json'
44
- with open(file_path, 'w') as json_file:
45
- json.dump(service_account, json_file)
46
- gspread_bot = gspread.service_account(filename='service_account.json')
47
- worksheet = gspread_bot.open("levelbot").sheet1
48
- test_merge_worksheet = gspread_bot.open("test_merge").sheet1
49
- """"""
50
- bot_ids = [1136614989411655780, 1166392942387265536, 1158038249835610123, 1130774761031610388, 1155489509518098565, 1155169841276260546, 1152238037355474964, 1154395078735953930]
51
- """"""
52
- api = HfApi()
53
- """"""
54
- global_df = pd.DataFrame()
55
- print(type(global_df))
56
- community_global_df = pd.DataFrame()
57
- community_global_df_with_id = pd.DataFrame()
58
- community_global_df_gradio = pd.DataFrame()
59
- test_merge = pd.read_csv("https://docs.google.com/spreadsheets/d/1C8aLqgCqLYcMiIFf-P_Aosaa03C_WLIB_UyqvjSdWg8/export?format=csv&gid=0")
60
-
61
-
62
- @bot.event
63
- async def on_ready():
64
- try:
65
- global global_df
66
- await asyncio.sleep(1.1)
67
- print(f'Logged in as {bot.user.name}')
68
- print(f"XP_PER_MESSAGE: {XP_PER_MESSAGE}")
69
-
70
- """import data from google sheets -> HF Space df (doesn't make API call this way, as it's read-only)"""
71
- global_df = test_merge
72
- print(f"csv successfully retrieved: \n {global_df}")
73
-
74
- # updates both leaderboards
75
- remove_huggingfolks.start()
76
- print("------------------------------------------------------------------------")
77
- except Exception as e:
78
- print(f"on_ready Error: {e}")
79
-
80
-
81
- def update_google_sheet():
82
- """save data from HF Space -> google sheets (makes 1 API call)"""
83
- try:
84
- print("Updating google sheets...")
85
- print("------------------------------------------------------------------------")
86
- name = "test_merge_worksheet"
87
- set_with_dataframe(test_merge_worksheet, global_df)
88
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
89
- print("------------------------------------------------------------------------")
90
- print(f"Google sheet {name} {test_merge_worksheet} successfully updated at {timestamp}! \n{global_df}")
91
- print("------------------------------------------------------------------------")
92
- except Exception as e:
93
- print(f"update_google_sheet Error: {e}")
94
-
95
- #@tasks.loop(minutes=1) tasks.loop leads to heartbeat blocked issues (merging calculations too much with normal discord bot functions)
96
- def update_hub_stats():
97
- try:
98
- global global_df
99
- print("Updating hub stats...")
100
- print("------------------------------------------------------------------------")
101
-
102
- for index, row in global_df.iterrows():
103
- user = row['hf_user_name']
104
- if pd.notna(user):
105
- #print(f"user: {user}")
106
- url = f"https://huggingface.co/api/users/{user}/overview"
107
- #print(f"url: {url}")
108
- response = requests.get(url)
109
- #print(f"response: {response}")
110
- if response.status_code == 200:
111
- data = response.json()
112
- #print(f"data: {data}")
113
- likes = data["numLikes"]
114
- models = data["numModels"]
115
- datasets = data["numDatasets"]
116
- spaces = data["numSpaces"]
117
- discussions = data["numDiscussions"]
118
- papers = data["numPapers"]
119
- upvotes = data["numUpvotes"]
120
-
121
- # recalculate level as well (no longer only depends on discord activity)
122
- try:
123
- global_df.loc[index, 'likes'] = likes
124
- global_df.loc[index, 'models'] = models
125
- global_df.loc[index, 'datasets'] = datasets
126
- global_df.loc[index, 'spaces'] = spaces
127
- global_df.loc[index, 'discussions'] = discussions
128
- global_df.loc[index, 'papers'] = papers
129
- global_df.loc[index, 'upvotes'] = upvotes
130
-
131
- total_hub_activity = likes + models + datasets + spaces + discussions + papers + upvotes
132
- total_hub_exp = total_hub_activity * 20 #2x better than discord
133
- total_hub_exp_string = "L" + str(total_hub_exp) + "L"
134
-
135
- # hub exp
136
- global_df.loc[index, 'hub_exp'] = total_hub_exp_string
137
-
138
- # total exp (discord + hub)
139
- discord_exp = row['discord_exp']
140
- discord_exp_int = discord_exp.strip('L')
141
- total_exp = int(discord_exp_int) + int(total_hub_exp)
142
- total_exp_string = "L" + str(total_exp) + "L"
143
- global_df.loc[index, 'total_exp'] = total_exp_string
144
-
145
- # level
146
- level = calculate_level(total_exp)
147
- global_df.loc[index, 'discord_level'] = level
148
-
149
- except Exception as e:
150
- print(f"{e} error updating the dataframe")
151
-
152
- else:
153
- print(f"Failed to retrieve data for user {user}. Status code: {response.status_code}")
154
-
155
-
156
- except Exception as e:
157
- print(f"Failed to parse data for user {user}. {e}")
158
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
159
- print("------------------------------------------------------------------------")
160
- print(f"Hub stats successfully updated at {timestamp}! \n{global_df}")
161
- print("------------------------------------------------------------------------")
162
-
163
- executor = ThreadPoolExecutor(max_workers=2)
164
- scheduler = BackgroundScheduler(executors={'default': executor})
165
- scheduler.add_job(update_google_sheet, trigger='interval', minutes=1, max_instances=2)
166
- scheduler.add_job(update_hub_stats, trigger='interval', minutes=1.5, max_instances=2)
167
- scheduler.start()
168
-
169
-
170
- def calculate_level(xp):
171
- return int(xp ** (1.0 / 3.0))
172
-
173
-
174
- def calculate_xp(level):
175
- return (int(level ** 3))
176
-
177
-
178
- async def add_exp(member_id):
179
- try:
180
- """Uses member_id to create new record or update old one [member_id_column] ... [member_exp_column]"""
181
- await asyncio.sleep(0.1)
182
- global global_df
183
- global community_global_df
184
-
185
- guild = bot.get_guild(879548962464493619)
186
- member = guild.get_member(member_id) # bot.get_user == User, guild.get_member == Member (not the same thing!)
187
-
188
- lvl1 = guild.get_role(1171861537699397733)
189
- lvl2 = guild.get_role(1171861595115245699)
190
- lvl3 = guild.get_role(1171861626715115591)
191
- lvl4 = guild.get_role(1171861657975259206)
192
- lvl5 = guild.get_role(1171861686580412497)
193
- lvl6 = guild.get_role(1171861900301172736)
194
- lvl7 = guild.get_role(1171861936258941018)
195
- lvl8 = guild.get_role(1171861968597024868)
196
- lvl9 = guild.get_role(1171862009982242836)
197
- lvl10 = guild.get_role(1164188093713223721)
198
-
199
- lvl11 = guild.get_role(1171524944354607104)
200
- lvl12 = guild.get_role(1171524990257082458)
201
- lvl13 = guild.get_role(1171525021928263791)
202
- lvl14 = guild.get_role(1171525062201966724)
203
- lvl15 = guild.get_role(1171525098465918996)
204
- lvl16 = guild.get_role(1176826165546201099)
205
- lvl17 = guild.get_role(1176826221301092392)
206
- lvl18 = guild.get_role(1176826260643659776)
207
- lvl19 = guild.get_role(1176826288816791693)
208
- lvl20 = guild.get_role(1176826319447801896)
209
-
210
- lvl21 = guild.get_role(1195030831174008902)
211
- lvl22 = guild.get_role(1195030883351150592)
212
- lvl23 = guild.get_role(1196055555006009445)
213
- lvl24 = guild.get_role(1196055640917938216)
214
- lvl25 = guild.get_role(1196055712506318869)
215
- lvl26 = guild.get_role(1196055775924195378)
216
- lvl27 = guild.get_role(1196055837018435664)
217
- lvl28 = guild.get_role(1196055908267081849)
218
- lvl29 = guild.get_role(1196055970804150352)
219
- lvl30 = guild.get_role(1196056027720847380)
220
-
221
- lvl31 = guild.get_role(1206542603261186078)
222
- lvl32 = guild.get_role(1206542673549205514)
223
- lvl33 = guild.get_role(1206542690939048007)
224
- lvl34 = guild.get_role(1206542707862806568)
225
- lvl35 = guild.get_role(1206542723633512468)
226
- lvl36 = guild.get_role(1206542738728681485)
227
- lvl37 = guild.get_role(1206542754625101866)
228
- lvl38 = guild.get_role(1206542771314364416)
229
- lvl39 = guild.get_role(1206542785973321758)
230
- lvl40 = guild.get_role(1206542802155208725)
231
-
232
- lvl41 = guild.get_role(1206568953221218354)
233
- lvl42 = guild.get_role(1206568979393413150)
234
- lvl43 = guild.get_role(1206568997374394368)
235
- lvl44 = guild.get_role(1206569014747463681)
236
- lvl45 = guild.get_role(1206569031650385921)
237
- lvl46 = guild.get_role(1206569047207182356)
238
- lvl47 = guild.get_role(1206569062851805254)
239
- lvl48 = guild.get_role(1206569077112315984)
240
- lvl49 = guild.get_role(1206569091826057306)
241
- lvl50 = guild.get_role(1206569107118493757)
242
-
243
- lvls = {
244
- 1: lvl1, 2: lvl2, 3: lvl3, 4: lvl4, 5: lvl5, 6: lvl6, 7: lvl7, 8: lvl8, 9: lvl9, 10: lvl10,
245
- 11: lvl11, 12: lvl12, 13: lvl13, 14: lvl14, 15: lvl15, 16: lvl16, 17: lvl17, 18: lvl18, 19: lvl19, 20: lvl20,
246
- 21: lvl21, 22: lvl22, 23: lvl23, 24: lvl24, 25: lvl25, 26: lvl26, 27: lvl27, 28: lvl28, 29: lvl29, 30: lvl30,
247
- 31: lvl31, 32: lvl32, 33: lvl33, 34: lvl34, 35: lvl35, 36: lvl36, 37: lvl37, 38: lvl38, 39: lvl39, 40: lvl40,
248
- 41: lvl41, 42: lvl42, 43: lvl43, 44: lvl44, 45: lvl45, 46: lvl46, 47: lvl47, 48: lvl48, 49: lvl49, 50: lvl50,
249
- }
250
-
251
- member_found = False
252
-
253
- print(f"Searching for member_id {member_id} in dataframe...")
254
- # discord_user_id column
255
- # iterate over items of first column (discord_user_id)
256
- for index, cell_value in global_df.iloc[:, 0].items():
257
- # tldr; set_as_dataframe forces scientific notation which corrupts discord_user_id data.
258
- # set_as_dataframe is still highly efficient (1 API call), so we format numerical data as strings,
259
- # which results in efficient google sheet updating + data integrity
260
- cell_value = str(cell_value)
261
- if cell_value.startswith("L") and cell_value.endswith("L"):
262
- cell_value_clipped = cell_value[1:-1]
263
- if cell_value_clipped == str(member_id): # str(member_id) needed, it is int by default
264
- print("test4")
265
- # if found, update that row...
266
- member_found = True
267
- print(f"Record for {member} found at row {index + 1}, column 1")
268
-
269
- # increment the old experience value (better not to replace outright)
270
- old_xp = global_df.loc[index, 'discord_exp']
271
-
272
- # remove L (write, so we replace)
273
- old_xp = str(old_xp)
274
- if old_xp.startswith("L") and old_xp.endswith("L"):
275
- old_xp = old_xp[1:-1]
276
-
277
- # str -> int temporarily for adding
278
- new_xp = int(old_xp) + XP_PER_MESSAGE
279
-
280
- total_exp = global_df.loc[index, 'total_exp']
281
- hub_xp = global_df.loc[index, 'hub_exp']
282
-
283
- total_exp = str(total_exp)
284
- hub_xp = str(hub_xp)
285
-
286
- if total_exp.startswith("L") and total_exp.endswith("L"):
287
- total_exp = total_exp[1:-1]
288
- if hub_xp.startswith("L") and hub_xp.endswith("L"):
289
- hub_xp = hub_xp[1:-1]
290
-
291
- # set old level; use this for more accurate logging and jumping multiple levels at once (for example, verifying)
292
- old_total_xp = int(total_exp)
293
- #old_level = calculate_level(old_total_xp)
294
-
295
- # check if hub exp not empty
296
- if hub_xp.strip():
297
- total_exp = int(new_xp) + int(hub_xp)
298
- else:
299
- total_exp = int(new_xp)
300
-
301
- # total v
302
- current_level = calculate_level(total_exp)
303
-
304
- # convert back to string + google sheet proofing
305
- new_xp = str(new_xp)
306
- if not new_xp.startswith("L") and not new_xp.endswith("L"):
307
- new_xp = "L" + str(new_xp) + "L"
308
- global_df.loc[index, 'discord_exp'] = new_xp # do not change column name
309
-
310
- # after
311
- total_exp = str(total_exp)
312
- if not total_exp.startswith("L") and not total_exp.endswith("L"):
313
- total_exp = "L" + str(total_exp) + "L"
314
-
315
- # add back to dataframe in memory after checking redundantly;
316
- if total_exp.startswith("L") and total_exp.endswith("L"):
317
- print("test5")
318
- global_df.loc[index, 'total_exp'] = total_exp # do not change column name
319
- print(f"Record for {member} updated from {old_total_xp} to {global_df.loc[index, 'total_exp']} (+{XP_PER_MESSAGE}) ")
320
-
321
- # level up
322
- verified_role = guild.get_role(900063512829755413)
323
-
324
- print(f"Current_level for {member}: {current_level}")
325
- if current_level >= 2 and current_level <=50:
326
- print("test6")
327
- current_role = lvls[current_level]
328
- if current_role not in member.roles: # if we need to level up / update role
329
- print("test7")
330
- # finding leaderboard rank + excluding huggingfolks (still need exclusion)
331
- try:
332
- print("Calculating rank...")
333
- copy_df = community_global_df.copy() # global_df works
334
-
335
- # check the initial DataFrame
336
- print("Initial copy_df:\n", copy_df.head())
337
-
338
- # discord_user_id to string
339
- copy_df['discord_user_id'] = copy_df['discord_user_id'].astype(str).str.strip('L')
340
- print("After processing discord_user_id:\n", copy_df['discord_user_id'].head())
341
- print("Data type of discord_user_id:", copy_df['discord_user_id'].dtype)
342
-
343
- # NaN -> 0
344
- copy_df['total_exp'] = copy_df['total_exp'].fillna(0)
345
-
346
- # total_exp to int
347
- copy_df['total_exp'] = copy_df['total_exp'].astype(str).str.strip('L').astype(int)
348
- print("After processing total_exp:\n", copy_df['total_exp'].head())
349
-
350
- # check processed DataFrame
351
- print("Processed copy_df:\n", copy_df.head())
352
-
353
- # if member_id exists in the DataFrame
354
- if str(member_id) in copy_df['discord_user_id'].values:
355
- print(f"Member ID {member_id} found in the DataFrame.")
356
- row = copy_df[copy_df['discord_user_id'] == str(member_id)]
357
- print(f"Row for member_id {member_id}:\n", row)
358
-
359
- # if the row is not empty
360
- if not row.empty:
361
- target_exp = row['total_exp'].values[0]
362
- print(f"Target experience for {member_id}: {target_exp}")
363
- rank = (copy_df['total_exp'] > target_exp).sum() + 1
364
- print(f"The rank for {member} based on total_exp is: {rank}")
365
- else:
366
- print(f"Row for member_id {member_id} is empty.")
367
- rank = "🤗"
368
- else:
369
- print(f"Discord ID {member} {member_id} not found in the DataFrame.")
370
- rank = "🤗"
371
- except Exception as e:
372
- print(f"An error occurred: {e}")
373
- rank = "🤗"
374
-
375
- # if level 3 -> then send embed, remove some exp
376
- if current_level >= 3: # could change to 4 maybe
377
- if verified_role not in member.roles:
378
-
379
- # L12345L -> `12345` -> 12345
380
- total_exp = total_exp[1:-1]
381
- total_exp = int(total_exp)
382
-
383
- if total_exp % 30 == 0: # staggers messages so we don't send one every time exp is earned
384
- # claim exp (-30 for level 3, but +100 as bonus exp. This scales infinitely until the member verifies,
385
- # so they can continue earning exp, it just won't translate to levels and the leaderboard.
386
- # This way they can claim at any time and get a big boost in levels!
387
- claim_exp = total_exp + 70
388
-
389
- # send embed
390
- embed = Embed(color=Color.red())
391
- embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
392
- embed.title = f"⚠️Your account is not Verified! Unable to level up `🚀` -> `{current_level}` ❌"
393
- msg = f'🤗 Hey {member}! You can continue leveling up in the Hugging Face Discord server by Verifying your account, and claim `{claim_exp}` bonus exp points!'
394
- embed.description = f"{msg}"
395
- verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
396
- embed.add_field(name="Verify Here:", value=verification_link, inline=True)
397
- u_1 = "👑 Earn exp for activity on Discord and HF and climb the ⁠leaderboard !"
398
- u_2 = "🌎 Feature your content in weekly news and increase its visibility!"
399
- u_3 = "🚀 Early access to Beta features!"
400
- u_4 = "🛡️ Secure your progress, and restore if needed!"
401
- embed.add_field(name="You can Unlock:", value=f"{u_1}\n{u_2}\n{u_3}\n{u_4}", inline=True)
402
- embed.set_image(url='https://cdn.discordapp.com/attachments/1150399343912833024/1205537451242688573/download_1.png?ex=65d8bb3e&is=65c6463e&hm=042fe7dd3521887db0bd48eeb846de1cc7c75194f9e95215c23512ff61ea3475&')
403
-
404
- lunar = bot.get_user(811235357663297546)
405
- await member.send(embed=embed)
406
- await lunar.send(embed=embed)
407
- print("Sent verification cap embed to {member}")
408
- print("------------------------------------------------------------------------")
409
- return
410
-
411
- # increment the old level value (better to replace outright)
412
- # only increment level column if you are lvl2 or 3+ with verified role (this may make some members not appear)
413
- global_df.loc[index, 'discord_level'] = current_level # do not change column name
414
-
415
- # remove all level roles then add new role
416
- current_level_roles = [role for level, role in lvls.items() if role in member.roles]
417
- if current_level_roles:
418
- print(f"current_level_roles for {member}: {current_level_roles}")
419
- for removable_role in current_level_roles:
420
- await member.remove_roles(removable_role)
421
- print(f"Removed {removable_role} from {member}")
422
- removable_role_name = removable_role.name
423
- else:
424
- removable_role_name = "🚀"
425
- await member.add_roles(current_role)
426
- print(f"Level Up! Gave {member} {current_role}")
427
-
428
- if current_level == 2: # special embed with opt-out instructions for discord newcomers
429
- embed = Embed(color=Color.blue())
430
- embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
431
- embed.title = f"Level Up! `{removable_role_name}` -> `{current_level}`"
432
- msg = f'🤗 Congrats {member}! You just leveled up in the Hugging Face Discord server. To opt out of these notifications, you can right click -> "Block" me!'
433
- embed.description = f"{msg}."
434
- embed.add_field(name="Leaderboard Ranking:", value=f"👑 **{rank}**\n\nhttps://discord.com/channels/879548962464493619/1197143964994773023", inline=True)
435
- msg3 = "- Posting\n- Reacting / being reacted to\n- Being active on the Hugging Face Hub (verify to link your Hub + Discord accounts!)"
436
- embed.add_field(name="How to Level Up:", value=msg3, inline=True)
437
- verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
438
- embed.add_field(name="Verify Here:", value=verification_link, inline=True)
439
- lunar = bot.get_user(811235357663297546)
440
- await member.send(embed=embed)
441
- await lunar.send(embed=embed)
442
- print(f"Sent levelup embed to {member}")
443
- return
444
-
445
- if current_role in member.roles:
446
- # send embed
447
- embed = Embed(color=Color.blue())
448
- embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
449
- embed.title = f"Level Up! `{removable_role_name}` -> `{current_level}`"
450
- msg = f'🤗 Congrats {member}! You just leveled up in the Hugging Face Discord server'
451
- embed.description = f"{msg}."
452
- embed.add_field(name="Leaderboard Ranking:", value=f"👑 **{rank}**\n\nhttps://discord.com/channels/879548962464493619/1197143964994773023", inline=True)
453
- msg3 = "- Posting\n- Reacting / being reacted to\n- Being active on the Hugging Face Hub (verify to link your Hub + Discord accounts!)"
454
- embed.add_field(name="How to Level Up:", value=msg3, inline=True)
455
- verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
456
- embed.add_field(name="Verify Here:", value=verification_link, inline=True)
457
-
458
- lunar = bot.get_user(811235357663297546)
459
- await member.send(embed=embed)
460
- await lunar.send(embed=embed)
461
- print(f"Sent levelup embed to {member}")
462
- print("------------------------------------------------------------------------")
463
- if not member_found: # this only checks if discord_user_id (with L) is present in discord_user_id column,
464
- # if not, create new record
465
- print(f"Creating new record for {member}")
466
-
467
- xp = 10 # define somewhere else?
468
- current_level = calculate_level(xp)
469
- xp = str(xp)
470
- if not xp.startswith("L") and not xp.endswith("L"):
471
- xp = "L" + str(xp) + "L"
472
- member_id = str(member_id)
473
- if not member_id.startswith("L") and not member_id.endswith("L"):
474
- member_id = "L" + str(member_id) + "L"
475
- member_name = str(member.name)
476
- hf_user_name = "n/a"
477
- hub_exp = "L0L"
478
- total_exp = xp
479
- verified_date = "n/a"
480
-
481
- # need to initialize these when creating new record
482
- likes = 0
483
- models = 0
484
- datasets = 0
485
- spaces = 0
486
- discussions = 0
487
- papers = 0
488
- upvotes = 0
489
-
490
- row_data = [member_id, member_name, xp, current_level, hf_user_name, hub_exp, total_exp, verified_date, likes, models, datasets, spaces, discussions, papers, upvotes]
491
- global_df.loc[len(global_df.index)] = row_data
492
-
493
- print("------------------------------------------------------------------------")
494
- except Exception as e:
495
- print(f"add_exp Error: {e}")
496
-
497
-
498
- @bot.event
499
- async def on_message(message):
500
- global global_df
501
- try:
502
- if message.author.id not in bot_ids: # could change to if author does not have bot role (roleid)
503
- print(f"adding exp from message {message.author}")
504
- await asyncio.sleep(0.1)
505
- await add_exp(message.author.id)
506
-
507
- # add check for verification
508
- if message.content.find("!help") != -1:
509
- await message.channel.send(
510
- "To verify your 🤗 account, message me '!auth <TOKEN>' using your API token found here: https://huggingface.co/settings/token"
511
- )
512
- if message.content.startswith('!auth'):
513
- await asyncio.sleep(3)
514
- lunar = bot.get_user(811235357663297546)
515
- token = message.content.split()[-1].strip()
516
- try:
517
- user = HfApi().whoami(token)
518
- except HTTPError as e:
519
- await message.channel.send(f"Error occured when trying to authenticate. Likely invalid API Token. {e}")
520
- token = "abc" # reset right after we use to be safe
521
-
522
- if user['type'] == 'org':
523
- await message.channel.send(
524
- "Authentication failed because you tried to authenticate with an organization's API token. Please authenticate with your User API token instead."
525
- )
526
- return
527
-
528
- try:
529
- server = bot.get_guild(879548962464493619)
530
- #role = discord.utils.get(server.roles, name="verified")
531
- role = server.get_role(900063512829755413)
532
- member = server.get_member(message.author.id)
533
- verified_date = datetime.now().strftime("%m/%d/%Y, %H:%M:%S")
534
- likes = 0
535
- models = 0
536
- datasets = 0
537
- spaces = 0
538
- discussions = 0
539
- papers = 0
540
- upvotes = 0
541
- # important!-----------------------------------------------------------------------------
542
- # In order to add verification to a discord account, some important checks must ALL pass:
543
- # 1. Discord account should not already have verified role.
544
- # 2. hf_user_name should not already exist in the dataframe.
545
- # -----> If it does, it means we are trying to link 1 HF account to multiple discord accounts. 1->many is disallowed.
546
-
547
- # check if the member has the verified role (prevent duplicate entries in google sheet)
548
- # if users want to change discord / HF accounts, we can deal with that manually
549
- if role in member.roles:
550
- await message.channel.send(f"{member} already has has the '{role}' role and is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
551
- return
552
-
553
- # check if hf_user_name in dataframe:
554
- if user['name'] in global_df['hf_user_name'].values:
555
- await message.channel.send(f"The HF account {user['name']} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
556
- return
557
-
558
- # check if discord_user_id in dataframe:
559
- altered_member_id = "L" + str(member.id) + "L"
560
- if altered_member_id in global_df['discord_user_id'].values:
561
-
562
- hf_user_name = global_df.loc[global_df['discord_user_id'] == altered_member_id, 'hf_user_name'].iloc[0]
563
-
564
- if pd.isnull(hf_user_name) or hf_user_name == 'n/a':
565
- # empty (no link created yet between discord_user_id and hf_user_name) so we can update
566
- global_df.loc[global_df['discord_user_id'] == altered_member_id, 'hf_user_name'] = user['name']
567
- global_df.loc[global_df['discord_user_id'] == altered_member_id, 'verified_date'] = verified_date
568
- await member.add_roles(role)
569
- if role in member.roles:
570
- print(f"Updated hf_user_name for id {member.id} | discord_user_name {member} | hf_user_name {user['name']}")
571
- await message.channel.send(f"Verification successful! [{member} <---> {user['name']}] 🤗")
572
- await lunar.send(f"Verification successful! [{member} <---> {user['name']}] 🤗")
573
- print(f"Verification successful! [{member} <---> {user['name']}] 🤗")
574
- return
575
- else:
576
- await message.channel.send(f"The Discord account {member} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
577
- await lunar.send(f"The Discord account {member} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
578
- print(f"The Discord account {member} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
579
- return
580
- except Exception as e:
581
- print(f"verification Error: {e}")
582
-
583
- # -----------------------------------------------------------------------------------------
584
- # If creating new record (edge case)
585
- xp = 10
586
- discord_exp = "L10L"
587
- hub_exp = "L0L"
588
- total_exp = "L10L"
589
- current_level = calculate_level(xp)
590
-
591
- row_data = [altered_member_id,
592
- member.name,
593
- discord_exp,
594
- current_level,
595
- user['name'],
596
- "L0L",
597
- hub_exp,
598
- total_exp,
599
- verified_date,
600
- likes,
601
- models,
602
- datasets,
603
- spaces,
604
- discussions,
605
- papers,
606
- upvotes]
607
-
608
- global_df.loc[len(global_df.index)] = row_data
609
- await member.add_roles(role)
610
- if role in member.roles:
611
- print(f"New record created for {member}")
612
- print("------------------------------------------------------------------------")
613
- await bot.process_commands(message)
614
- except Exception as e:
615
- print(f"on_message Error: {e}")
616
-
617
-
618
- @bot.event
619
- async def on_reaction_add(reaction, user):
620
- try:
621
- if user.id not in bot_ids:
622
- print(f"adding exp from react {user}")
623
- await asyncio.sleep(0.1)
624
- if not isinstance(reaction.message.channel, discord.DMChannel): # can't earn exp from reacting in bot DMs, which is harder to track
625
- await add_exp(user.id)
626
- await asyncio.sleep(0.1)
627
- if reaction.message.author.id != user.id: # can't earn while self-reacting, which is abuseable
628
- await add_exp(reaction.message.author.id)
629
- except Exception as e:
630
- print(f"on_reaction_add Error: {e}")
631
-
632
-
633
- def format_hyperlink(username):
634
- return f'<a target="_blank" href="https://huggingface.co/{username}" style="color: var(--link-text-color);">{username}</a>'
635
-
636
-
637
- @tasks.loop(minutes=1)
638
- async def remove_huggingfolks():
639
- try:
640
- # remove huggingfolks
641
- global community_global_df
642
- global community_global_df_with_id
643
- global community_global_df_gradio
644
-
645
- community_global_df = global_df.copy()
646
-
647
- guild = bot.get_guild(879548962464493619)
648
- role = discord.utils.get(guild.roles, id=897376942817419265)
649
- members_with_role = [member.id for member in guild.members if role in member.roles]
650
-
651
- # remove L formatting (doesn't affect main global_df)
652
- community_global_df['discord_user_id'] = community_global_df['discord_user_id'].str.strip('L').astype(str)
653
-
654
- for member_id in members_with_role:
655
- community_global_df = community_global_df[community_global_df.iloc[:, 0] != str(member_id)]
656
-
657
- # make a copy while discord id column still exists -> use for rank in discord embeds
658
- community_global_df_with_id = community_global_df_with_id.copy()
659
-
660
- # reorder (switching from discord_user_name to hf_user_name)
661
- reorder = ['discord_user_id', 'hf_user_name', 'discord_exp','discord_level', 'discord_user_name', 'hub_exp', 'total_exp', 'verified_date']
662
- community_global_df = community_global_df[reorder]
663
-
664
- # drop first column (discord id -> this is so we can display the important stuff in the leaderboard)
665
- community_global_df.drop(community_global_df.columns[0], axis=1, inplace=True)
666
- community_global_df.drop(community_global_df.columns[1], axis=1, inplace=True)
667
- community_global_df.drop(community_global_df.columns[2], axis=1, inplace=True)
668
- community_global_df.drop(community_global_df.columns[2], axis=1, inplace=True)
669
- community_global_df.drop(community_global_df.columns[3], axis=1, inplace=True)
670
- # drop rows (records) where hf_user_name does not exist (we display only verified users)
671
- community_global_df = community_global_df.dropna(subset=['hf_user_name'])
672
- community_global_df['total_exp'] = community_global_df['total_exp'].str.strip('L').astype(int)
673
- community_global_df['total_exp'] = pd.to_numeric(community_global_df['total_exp'], errors='coerce').fillna(0).astype(int)
674
- community_global_df = community_global_df.nlargest(len(community_global_df), 'total_exp')
675
- # add profile hyperlinks to gradio demo
676
- community_global_df_gradio = community_global_df.copy() # must be a COPY, otherwise it adds a new name for the same dataframe.
677
- community_global_df_gradio['hf_user_name'] = community_global_df_gradio['hf_user_name'].apply(format_hyperlink)
678
-
679
- top_num = 30
680
- top_num_exp = community_global_df.nlargest(top_num, 'total_exp')
681
-
682
- top_num_exp['D'] = ['🥇','🥈','🥉','','','','','','','','','','','','','','','','','','','','','','','','','','','']
683
- top_num_rows = top_num_exp.values.tolist()
684
-
685
- #print(top_30_rows)
686
- channel = bot.get_channel(1197143964994773023)
687
- message = await channel.fetch_message(1197148293164187678)
688
-
689
- # put into message / leaderboard
690
- new_table = tabulate(top_num_rows, headers=["Name", "Level", "Experience", "Rank"], tablefmt="plain")
691
- await message.edit(content=f"Updated Leaderboard:\n```\n{new_table}\n```")
692
- print("Updated discord leaderboard!")
693
- print("------------------------------------------------------------------------")
694
- except Exception as e:
695
- print(f"remove_huggingfolks Error: {e}")
696
-
697
-
698
- @bot.command(name='xp_help')
699
- async def xp_help(ctx):
700
- try:
701
- help_message = "How to earn Discord / Hub exp: Post messages, react, Like, discuss, create repos and papers"
702
- await ctx.author.send(help_message)
703
- except Exception as e:
704
- print(f"xp_help Error: {e}")
705
-
706
-
707
- @bot.command()
708
- async def count_users_with_role(ctx, role_id):
709
- role = discord.utils.get(ctx.guild.roles, id=int(role_id))
710
- count = sum(1 for member in ctx.guild.members if role in member.roles)
711
- await ctx.send(f"Number of users with the role '{role.name}': {count}")
712
-
713
-
714
- DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None)
715
- def run_bot():
716
- bot.run(DISCORD_TOKEN)
717
- threading.Thread(target=run_bot).start()
718
-
719
-
720
- def get_data():
721
- try:
722
- return community_global_df_gradio
723
- except Exception as e:
724
- print(f"get_data Error: {e}")
725
-
726
-
727
- def get_data2():
728
- try:
729
- display_data = {
730
- '🤗 Hub (+30 exp)': ['Creating Repos', 'Papers', 'Likes/Upvotes', 'Discussions'],
731
- 'Discord (+10 exp)': ['Posting messages', 'Reacting', '', '']
732
- }
733
- display_df = pd.DataFrame(display_data)
734
- return display_df
735
- except Exception as e:
736
- print(f"get_data2 Error: {e}")
737
-
738
-
739
- demo = gr.Blocks()
740
- with demo:
741
- try:
742
- dataframe1 = pd.read_csv("https://docs.google.com/spreadsheets/d/1C8aLqgCqLYcMiIFf-P_Aosaa03C_WLIB_UyqvjSdWg8/export?format=csv&gid=0") # required for timing
743
- print(dataframe1)
744
-
745
- level_counts = dataframe1['discord_level'].value_counts().sort_index()
746
- dataframe2 = pd.DataFrame({
747
- 'Levels': level_counts.index,
748
- 'Members': level_counts.values
749
- })
750
- print(dataframe2)
751
-
752
- TITLE = """<h1 align="center" id="space-title">🤗 Hugging Face Level Leaderboard</h1>"""
753
- gr.HTML(TITLE)
754
- with gr.Tabs(elem_classes="tab-buttons") as tabs:
755
- with gr.TabItem("🏅 Level leaderboard", elem_id="level-table", id=0):
756
- #gr.Markdown("# 📈 Experience Leaderboard")
757
- with gr.Row():
758
- with gr.Column():
759
- gr.DataFrame(get_data, every=5, height=500, datatype=["markdown"], interactive=False, col_count=(3, "fixed"), column_widths=["100px","100px","100px"])
760
-
761
- with gr.Column():
762
- gr.BarPlot(
763
- value=dataframe2,
764
- x="Levels",
765
- y="Members",
766
- title="Level Distribution",
767
- height=450,
768
- width=450,
769
- interactive=False
770
- )
771
- with gr.Row():
772
- gr.Markdown("# 📈 How to earn Experience!")
773
- with gr.Row():
774
- gr.DataFrame(get_data2, every=5, interactive=False)
775
- #with gr.TabItem("📈 Members of the Week", elem_id="week-table", id=1):
776
-
777
- #with gr.TabItem("📈 Hub-only leaderboard", elem_id="hub-table", id=2):
778
- except Exception as e:
779
- print(f"gradio demo Error: {e}")
780
  demo.queue().launch()
 
1
+ import asyncio
2
+ import csv
3
+ import datetime
4
+ import json
5
+ import logging
6
+ import os
7
+ import os.path
8
+ import random
9
+ import re
10
+ import threading
11
+ import time
12
+
13
+ import discord
14
+ import gradio as gr
15
+ import gradio_client
16
+ import gspread
17
+ import numpy as np
18
+ import pandas as pd
19
+ import requests
20
+
21
+ from requests import HTTPError
22
+ from apscheduler.executors.pool import ThreadPoolExecutor
23
+ from apscheduler.schedulers.background import BackgroundScheduler
24
+ from discord import Color, Embed
25
+ from discord.ext import commands, tasks
26
+ from gradio_client import Client
27
+ from gspread_dataframe import get_as_dataframe, set_with_dataframe
28
+ from gspread_formatting.dataframe import format_with_dataframe
29
+ from huggingface_hub import HfApi, list_liked_repos, list_metrics, list_models
30
+ from tabulate import tabulate
31
+ from datetime import datetime
32
+
33
+
34
+ DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None)
35
+ intents = discord.Intents.all()
36
+ bot = commands.Bot(command_prefix='!', intents=intents)
37
+
38
+
39
+ """"""
40
+ XP_PER_MESSAGE = 10 # 100k messages = 1M exp = lvl 100
41
+ """"""
42
+ service_account = json.loads(os.environ.get('KEY'))
43
+ file_path = 'service_account.json'
44
+ with open(file_path, 'w') as json_file:
45
+ json.dump(service_account, json_file)
46
+ gspread_bot = gspread.service_account(filename='service_account.json')
47
+ worksheet = gspread_bot.open("levelbot").sheet1
48
+ test_merge_worksheet = gspread_bot.open("test_merge").sheet1
49
+ """"""
50
+ bot_ids = [1136614989411655780, 1166392942387265536, 1158038249835610123, 1130774761031610388, 1155489509518098565, 1155169841276260546, 1152238037355474964, 1154395078735953930]
51
+ """"""
52
+ api = HfApi()
53
+ """"""
54
+ global_df = pd.DataFrame()
55
+ print(type(global_df))
56
+ community_global_df = pd.DataFrame()
57
+ community_global_df_with_id = pd.DataFrame()
58
+ community_global_df_gradio = pd.DataFrame()
59
+ test_merge = pd.read_csv("https://docs.google.com/spreadsheets/d/1C8aLqgCqLYcMiIFf-P_Aosaa03C_WLIB_UyqvjSdWg8/export?format=csv&gid=0")
60
+
61
+
62
+ @bot.event
63
+ async def on_ready():
64
+ try:
65
+ global global_df
66
+ await asyncio.sleep(1.1)
67
+ print(f'Logged in as {bot.user.name}')
68
+ print(f"XP_PER_MESSAGE: {XP_PER_MESSAGE}")
69
+
70
+ """import data from google sheets -> HF Space df (doesn't make API call this way, as it's read-only)"""
71
+ global_df = test_merge
72
+ print(f"csv successfully retrieved: \n {global_df}")
73
+
74
+ # updates both leaderboards
75
+ remove_huggingfolks.start()
76
+ print("------------------------------------------------------------------------")
77
+ except Exception as e:
78
+ print(f"on_ready Error: {e}")
79
+
80
+
81
+ def update_google_sheet():
82
+ """save data from HF Space -> google sheets (makes 1 API call)"""
83
+ try:
84
+ print("Updating google sheets...")
85
+ print("------------------------------------------------------------------------")
86
+ name = "test_merge_worksheet"
87
+ set_with_dataframe(test_merge_worksheet, global_df)
88
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
89
+ print("------------------------------------------------------------------------")
90
+ print(f"Google sheet {name} {test_merge_worksheet} successfully updated at {timestamp}! \n{global_df}")
91
+ print("------------------------------------------------------------------------")
92
+ except Exception as e:
93
+ print(f"update_google_sheet Error: {e}")
94
+
95
+ #@tasks.loop(minutes=1) tasks.loop leads to heartbeat blocked issues (merging calculations too much with normal discord bot functions)
96
+ def update_hub_stats():
97
+ try:
98
+ global global_df
99
+ print("Updating hub stats...")
100
+ print("------------------------------------------------------------------------")
101
+
102
+ for index, row in global_df.iterrows():
103
+ user = row['hf_user_name']
104
+ if pd.notna(user):
105
+ #print(f"user: {user}")
106
+ url = f"https://huggingface.co/api/users/{user}/overview"
107
+ #print(f"url: {url}")
108
+ response = requests.get(url)
109
+ #print(f"response: {response}")
110
+ if response.status_code == 200:
111
+ data = response.json()
112
+ #print(f"data: {data}")
113
+ likes = data["numLikes"]
114
+ models = data["numModels"]
115
+ datasets = data["numDatasets"]
116
+ spaces = data["numSpaces"]
117
+ discussions = data["numDiscussions"]
118
+ papers = data["numPapers"]
119
+ upvotes = data["numUpvotes"]
120
+
121
+ # recalculate level as well (no longer only depends on discord activity)
122
+ try:
123
+ global_df.loc[index, 'likes'] = likes
124
+ global_df.loc[index, 'models'] = models
125
+ global_df.loc[index, 'datasets'] = datasets
126
+ global_df.loc[index, 'spaces'] = spaces
127
+ global_df.loc[index, 'discussions'] = discussions
128
+ global_df.loc[index, 'papers'] = papers
129
+ global_df.loc[index, 'upvotes'] = upvotes
130
+
131
+ total_hub_activity = likes + models + datasets + spaces + discussions + papers + upvotes
132
+ total_hub_exp = total_hub_activity * 20 #2x better than discord
133
+ total_hub_exp_string = "L" + str(total_hub_exp) + "L"
134
+
135
+ # hub exp
136
+ global_df.loc[index, 'hub_exp'] = total_hub_exp_string
137
+
138
+ # total exp (discord + hub)
139
+ discord_exp = row['discord_exp']
140
+ discord_exp_int = discord_exp.strip('L')
141
+ total_exp = int(discord_exp_int) + int(total_hub_exp)
142
+ total_exp_string = "L" + str(total_exp) + "L"
143
+ global_df.loc[index, 'total_exp'] = total_exp_string
144
+
145
+ # level
146
+ level = calculate_level(total_exp)
147
+ global_df.loc[index, 'discord_level'] = level
148
+
149
+ except Exception as e:
150
+ print(f"{e} error updating the dataframe")
151
+
152
+ else:
153
+ print(f"Failed to retrieve data for user {user}. Status code: {response.status_code}")
154
+
155
+
156
+ except Exception as e:
157
+ print(f"Failed to parse data for user {user}. {e}")
158
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
159
+ print("------------------------------------------------------------------------")
160
+ print(f"Hub stats successfully updated at {timestamp}! \n{global_df}")
161
+ print("------------------------------------------------------------------------")
162
+
163
+ executor = ThreadPoolExecutor(max_workers=2)
164
+ scheduler = BackgroundScheduler(executors={'default': executor})
165
+ scheduler.add_job(update_google_sheet, trigger='interval', minutes=1, max_instances=2)
166
+ scheduler.add_job(update_hub_stats, trigger='interval', minutes=1.5, max_instances=2)
167
+ scheduler.start()
168
+
169
+
170
+ def calculate_level(xp):
171
+ return int(xp ** (1.0 / 3.0))
172
+
173
+
174
+ def calculate_xp(level):
175
+ return (int(level ** 3))
176
+
177
+
178
+ async def add_exp(member_id):
179
+ try:
180
+ """Uses member_id to create new record or update old one [member_id_column] ... [member_exp_column]"""
181
+ await asyncio.sleep(0.1)
182
+ global global_df
183
+ global community_global_df
184
+
185
+ guild = bot.get_guild(879548962464493619)
186
+ member = guild.get_member(member_id) # bot.get_user == User, guild.get_member == Member (not the same thing!)
187
+
188
+ lvl1 = guild.get_role(1171861537699397733)
189
+ lvl2 = guild.get_role(1171861595115245699)
190
+ lvl3 = guild.get_role(1171861626715115591)
191
+ lvl4 = guild.get_role(1171861657975259206)
192
+ lvl5 = guild.get_role(1171861686580412497)
193
+ lvl6 = guild.get_role(1171861900301172736)
194
+ lvl7 = guild.get_role(1171861936258941018)
195
+ lvl8 = guild.get_role(1171861968597024868)
196
+ lvl9 = guild.get_role(1171862009982242836)
197
+ lvl10 = guild.get_role(1164188093713223721)
198
+
199
+ lvl11 = guild.get_role(1171524944354607104)
200
+ lvl12 = guild.get_role(1171524990257082458)
201
+ lvl13 = guild.get_role(1171525021928263791)
202
+ lvl14 = guild.get_role(1171525062201966724)
203
+ lvl15 = guild.get_role(1171525098465918996)
204
+ lvl16 = guild.get_role(1176826165546201099)
205
+ lvl17 = guild.get_role(1176826221301092392)
206
+ lvl18 = guild.get_role(1176826260643659776)
207
+ lvl19 = guild.get_role(1176826288816791693)
208
+ lvl20 = guild.get_role(1176826319447801896)
209
+
210
+ lvl21 = guild.get_role(1195030831174008902)
211
+ lvl22 = guild.get_role(1195030883351150592)
212
+ lvl23 = guild.get_role(1196055555006009445)
213
+ lvl24 = guild.get_role(1196055640917938216)
214
+ lvl25 = guild.get_role(1196055712506318869)
215
+ lvl26 = guild.get_role(1196055775924195378)
216
+ lvl27 = guild.get_role(1196055837018435664)
217
+ lvl28 = guild.get_role(1196055908267081849)
218
+ lvl29 = guild.get_role(1196055970804150352)
219
+ lvl30 = guild.get_role(1196056027720847380)
220
+
221
+ lvl31 = guild.get_role(1206542603261186078)
222
+ lvl32 = guild.get_role(1206542673549205514)
223
+ lvl33 = guild.get_role(1206542690939048007)
224
+ lvl34 = guild.get_role(1206542707862806568)
225
+ lvl35 = guild.get_role(1206542723633512468)
226
+ lvl36 = guild.get_role(1206542738728681485)
227
+ lvl37 = guild.get_role(1206542754625101866)
228
+ lvl38 = guild.get_role(1206542771314364416)
229
+ lvl39 = guild.get_role(1206542785973321758)
230
+ lvl40 = guild.get_role(1206542802155208725)
231
+
232
+ lvl41 = guild.get_role(1206568953221218354)
233
+ lvl42 = guild.get_role(1206568979393413150)
234
+ lvl43 = guild.get_role(1206568997374394368)
235
+ lvl44 = guild.get_role(1206569014747463681)
236
+ lvl45 = guild.get_role(1206569031650385921)
237
+ lvl46 = guild.get_role(1206569047207182356)
238
+ lvl47 = guild.get_role(1206569062851805254)
239
+ lvl48 = guild.get_role(1206569077112315984)
240
+ lvl49 = guild.get_role(1206569091826057306)
241
+ lvl50 = guild.get_role(1206569107118493757)
242
+
243
+ lvls = {
244
+ 1: lvl1, 2: lvl2, 3: lvl3, 4: lvl4, 5: lvl5, 6: lvl6, 7: lvl7, 8: lvl8, 9: lvl9, 10: lvl10,
245
+ 11: lvl11, 12: lvl12, 13: lvl13, 14: lvl14, 15: lvl15, 16: lvl16, 17: lvl17, 18: lvl18, 19: lvl19, 20: lvl20,
246
+ 21: lvl21, 22: lvl22, 23: lvl23, 24: lvl24, 25: lvl25, 26: lvl26, 27: lvl27, 28: lvl28, 29: lvl29, 30: lvl30,
247
+ 31: lvl31, 32: lvl32, 33: lvl33, 34: lvl34, 35: lvl35, 36: lvl36, 37: lvl37, 38: lvl38, 39: lvl39, 40: lvl40,
248
+ 41: lvl41, 42: lvl42, 43: lvl43, 44: lvl44, 45: lvl45, 46: lvl46, 47: lvl47, 48: lvl48, 49: lvl49, 50: lvl50,
249
+ }
250
+
251
+ member_found = False
252
+
253
+ print(f"Searching for member_id {member_id} in dataframe...")
254
+ # discord_user_id column
255
+ # iterate over items of first column (discord_user_id)
256
+ for index, cell_value in global_df.iloc[:, 0].items():
257
+ # tldr; set_as_dataframe forces scientific notation which corrupts discord_user_id data.
258
+ # set_as_dataframe is still highly efficient (1 API call), so we format numerical data as strings,
259
+ # which results in efficient google sheet updating + data integrity
260
+ cell_value = str(cell_value)
261
+ if cell_value.startswith("L") and cell_value.endswith("L"):
262
+ cell_value_clipped = cell_value[1:-1]
263
+ if cell_value_clipped == str(member_id): # str(member_id) needed, it is int by default
264
+ print("test4")
265
+ # if found, update that row...
266
+ member_found = True
267
+ print(f"Record for {member} found at row {index + 1}, column 1")
268
+
269
+ # increment the old experience value (better not to replace outright)
270
+ old_xp = global_df.loc[index, 'discord_exp']
271
+
272
+ # remove L (write, so we replace)
273
+ old_xp = str(old_xp)
274
+ if old_xp.startswith("L") and old_xp.endswith("L"):
275
+ old_xp = old_xp[1:-1]
276
+
277
+ # str -> int temporarily for adding
278
+ new_xp = int(old_xp) + XP_PER_MESSAGE
279
+
280
+ total_exp = global_df.loc[index, 'total_exp']
281
+ hub_xp = global_df.loc[index, 'hub_exp']
282
+
283
+ total_exp = str(total_exp)
284
+ hub_xp = str(hub_xp)
285
+
286
+ if total_exp.startswith("L") and total_exp.endswith("L"):
287
+ total_exp = total_exp[1:-1]
288
+ if hub_xp.startswith("L") and hub_xp.endswith("L"):
289
+ hub_xp = hub_xp[1:-1]
290
+
291
+ # set old level; use this for more accurate logging and jumping multiple levels at once (for example, verifying)
292
+ old_total_xp = int(total_exp)
293
+ #old_level = calculate_level(old_total_xp)
294
+
295
+ # check if hub exp not empty
296
+ if hub_xp.strip():
297
+ total_exp = int(new_xp) + int(hub_xp)
298
+ else:
299
+ total_exp = int(new_xp)
300
+
301
+ # total v
302
+ current_level = calculate_level(total_exp)
303
+
304
+ # convert back to string + google sheet proofing
305
+ new_xp = str(new_xp)
306
+ if not new_xp.startswith("L") and not new_xp.endswith("L"):
307
+ new_xp = "L" + str(new_xp) + "L"
308
+ global_df.loc[index, 'discord_exp'] = new_xp # do not change column name
309
+
310
+ # after
311
+ total_exp = str(total_exp)
312
+ if not total_exp.startswith("L") and not total_exp.endswith("L"):
313
+ total_exp = "L" + str(total_exp) + "L"
314
+
315
+ # add back to dataframe in memory after checking redundantly;
316
+ if total_exp.startswith("L") and total_exp.endswith("L"):
317
+ print("test5")
318
+ global_df.loc[index, 'total_exp'] = total_exp # do not change column name
319
+ print(f"Record for {member} updated from {old_total_xp} to {global_df.loc[index, 'total_exp']} (+{XP_PER_MESSAGE}) ")
320
+
321
+ # level up
322
+ verified_role = guild.get_role(900063512829755413)
323
+
324
+ print(f"Current_level for {member}: {current_level}")
325
+ if current_level >= 2 and current_level <=50:
326
+ print("test6")
327
+ current_role = lvls[current_level]
328
+ if current_role not in member.roles: # if we need to level up / update role
329
+ print("test7")
330
+ # finding leaderboard rank + excluding huggingfolks (still need exclusion)
331
+ try:
332
+ print("Calculating rank...")
333
+ copy_df = community_global_df.copy() # global_df works
334
+
335
+ # check the initial DataFrame
336
+ print("Initial copy_df:\n", copy_df.head())
337
+
338
+ # discord_user_id to string
339
+ copy_df['discord_user_id'] = copy_df['discord_user_id'].astype(str).str.strip('L')
340
+ print("After processing discord_user_id:\n", copy_df['discord_user_id'].head())
341
+ print("Data type of discord_user_id:", copy_df['discord_user_id'].dtype)
342
+
343
+ # NaN -> 0
344
+ copy_df['total_exp'] = copy_df['total_exp'].fillna(0)
345
+
346
+ # total_exp to int
347
+ copy_df['total_exp'] = copy_df['total_exp'].astype(str).str.strip('L').astype(int)
348
+ print("After processing total_exp:\n", copy_df['total_exp'].head())
349
+
350
+ # check processed DataFrame
351
+ print("Processed copy_df:\n", copy_df.head())
352
+
353
+ # if member_id exists in the DataFrame
354
+ if str(member_id) in copy_df['discord_user_id'].values:
355
+ print(f"Member ID {member_id} found in the DataFrame.")
356
+ row = copy_df[copy_df['discord_user_id'] == str(member_id)]
357
+ print(f"Row for member_id {member_id}:\n", row)
358
+
359
+ # if the row is not empty
360
+ if not row.empty:
361
+ target_exp = row['total_exp'].values[0]
362
+ print(f"Target experience for {member_id}: {target_exp}")
363
+ rank = (copy_df['total_exp'] > target_exp).sum() + 1
364
+ print(f"The rank for {member} based on total_exp is: {rank}")
365
+ else:
366
+ print(f"Row for member_id {member_id} is empty.")
367
+ rank = "🤗"
368
+ else:
369
+ print(f"Discord ID {member} {member_id} not found in the DataFrame.")
370
+ rank = "🤗"
371
+ except Exception as e:
372
+ print(f"An error occurred: {e}")
373
+ rank = "🤗"
374
+
375
+ # if level 3 -> then send embed, remove some exp
376
+ if current_level >= 3: # could change to 4 maybe
377
+ if verified_role not in member.roles:
378
+
379
+ # L12345L -> `12345` -> 12345
380
+ total_exp = total_exp[1:-1]
381
+ total_exp = int(total_exp)
382
+
383
+ if total_exp % 30 == 0: # staggers messages so we don't send one every time exp is earned
384
+ # claim exp (-30 for level 3, but +100 as bonus exp. This scales infinitely until the member verifies,
385
+ # so they can continue earning exp, it just won't translate to levels and the leaderboard.
386
+ # This way they can claim at any time and get a big boost in levels!
387
+ claim_exp = total_exp + 70
388
+
389
+ # send embed
390
+ embed = Embed(color=Color.red())
391
+ embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
392
+ embed.title = f"⚠️Your account is not Verified! Unable to level up `🚀` -> `{current_level}` ❌"
393
+ msg = f'🤗 Hey {member}! You can continue leveling up in the Hugging Face Discord server by Verifying your account, and claim `{claim_exp}` bonus exp points!'
394
+ embed.description = f"{msg}"
395
+ verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
396
+ embed.add_field(name="Verify Here:", value=verification_link, inline=True)
397
+ u_1 = "👑 Earn exp for activity on Discord and HF and climb the ⁠leaderboard !"
398
+ u_2 = "🌎 Feature your content in weekly news and increase its visibility!"
399
+ u_3 = "🚀 Early access to Beta features!"
400
+ u_4 = "🛡️ Secure your progress, and restore if needed!"
401
+ embed.add_field(name="You can Unlock:", value=f"{u_1}\n{u_2}\n{u_3}\n{u_4}", inline=True)
402
+ embed.set_image(url='https://cdn.discordapp.com/attachments/1150399343912833024/1205537451242688573/download_1.png?ex=65d8bb3e&is=65c6463e&hm=042fe7dd3521887db0bd48eeb846de1cc7c75194f9e95215c23512ff61ea3475&')
403
+
404
+ lunar = bot.get_user(811235357663297546)
405
+ await member.send(embed=embed)
406
+ await lunar.send(embed=embed)
407
+ print("Sent verification cap embed to {member}")
408
+ print("------------------------------------------------------------------------")
409
+ return
410
+
411
+ # increment the old level value (better to replace outright)
412
+ # only increment level column if you are lvl2 or 3+ with verified role (this may make some members not appear)
413
+ global_df.loc[index, 'discord_level'] = current_level # do not change column name
414
+
415
+ # remove all level roles then add new role
416
+ current_level_roles = [role for level, role in lvls.items() if role in member.roles]
417
+ if current_level_roles:
418
+ print(f"current_level_roles for {member}: {current_level_roles}")
419
+ for removable_role in current_level_roles:
420
+ await member.remove_roles(removable_role)
421
+ print(f"Removed {removable_role} from {member}")
422
+ removable_role_name = removable_role.name
423
+ else:
424
+ removable_role_name = "🚀"
425
+ await member.add_roles(current_role)
426
+ print(f"Level Up! Gave {member} {current_role}")
427
+
428
+ if current_level == 2: # special embed with opt-out instructions for discord newcomers
429
+ embed = Embed(color=Color.blue())
430
+ embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
431
+ embed.title = f"Level Up! `{removable_role_name}` -> `{current_level}`"
432
+ msg = f'🤗 Congrats {member}! You just leveled up in the Hugging Face Discord server. To opt out of these notifications, you can right click -> "Block" me!'
433
+ embed.description = f"{msg}."
434
+ embed.add_field(name="Leaderboard Ranking:", value=f"👑 **{rank}**\n\nhttps://discord.com/channels/879548962464493619/1197143964994773023", inline=True)
435
+ msg3 = "- Posting\n- Reacting / being reacted to\n- Being active on the Hugging Face Hub (verify to link your Hub + Discord accounts!)"
436
+ embed.add_field(name="How to Level Up:", value=msg3, inline=True)
437
+ verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
438
+ embed.add_field(name="Verify Here:", value=verification_link, inline=True)
439
+ lunar = bot.get_user(811235357663297546)
440
+ await member.send(embed=embed)
441
+ await lunar.send(embed=embed)
442
+ print(f"Sent levelup embed to {member}")
443
+ return
444
+
445
+ if current_role in member.roles:
446
+ # send embed
447
+ embed = Embed(color=Color.blue())
448
+ embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
449
+ embed.title = f"Level Up! `{removable_role_name}` -> `{current_level}`"
450
+ msg = f'🤗 Congrats {member}! You just leveled up in the Hugging Face Discord server'
451
+ embed.description = f"{msg}."
452
+ embed.add_field(name="Leaderboard Ranking:", value=f"👑 **{rank}**\n\nhttps://discord.com/channels/879548962464493619/1197143964994773023", inline=True)
453
+ msg3 = "- Posting\n- Reacting / being reacted to\n- Being active on the Hugging Face Hub (verify to link your Hub + Discord accounts!)"
454
+ embed.add_field(name="How to Level Up:", value=msg3, inline=True)
455
+ verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
456
+ embed.add_field(name="Verify Here:", value=verification_link, inline=True)
457
+
458
+ lunar = bot.get_user(811235357663297546)
459
+ await member.send(embed=embed)
460
+ await lunar.send(embed=embed)
461
+ print(f"Sent levelup embed to {member}")
462
+ print("------------------------------------------------------------------------")
463
+ if not member_found: # this only checks if discord_user_id (with L) is present in discord_user_id column,
464
+ # if not, create new record
465
+ print(f"Creating new record for {member}")
466
+
467
+ xp = 10 # define somewhere else?
468
+ current_level = calculate_level(xp)
469
+ xp = str(xp)
470
+ if not xp.startswith("L") and not xp.endswith("L"):
471
+ xp = "L" + str(xp) + "L"
472
+ member_id = str(member_id)
473
+ if not member_id.startswith("L") and not member_id.endswith("L"):
474
+ member_id = "L" + str(member_id) + "L"
475
+ member_name = str(member.name)
476
+ hf_user_name = "n/a"
477
+ hub_exp = "L0L"
478
+ total_exp = xp
479
+ verified_date = "n/a"
480
+
481
+ # need to initialize these when creating new record
482
+ likes = 0
483
+ models = 0
484
+ datasets = 0
485
+ spaces = 0
486
+ discussions = 0
487
+ papers = 0
488
+ upvotes = 0
489
+
490
+ row_data = [member_id, member_name, xp, current_level, hf_user_name, hub_exp, total_exp, verified_date, likes, models, datasets, spaces, discussions, papers, upvotes]
491
+ global_df.loc[len(global_df.index)] = row_data
492
+
493
+ print("------------------------------------------------------------------------")
494
+ except Exception as e:
495
+ print(f"add_exp Error: {e}")
496
+
497
+
498
+ @bot.event
499
+ async def on_message(message):
500
+ global global_df
501
+ try:
502
+ if message.author.id not in bot_ids: # could change to if author does not have bot role (roleid)
503
+ print(f"adding exp from message {message.author}")
504
+ await asyncio.sleep(0.1)
505
+ await add_exp(message.author.id)
506
+
507
+ # add check for verification
508
+ if message.content.find("!help") != -1:
509
+ await message.channel.send(
510
+ "To verify your 🤗 account, message me '!auth <TOKEN>' using your API token found here: https://huggingface.co/settings/token"
511
+ )
512
+ if message.content.startswith('!auth'):
513
+ await asyncio.sleep(3)
514
+ lunar = bot.get_user(811235357663297546)
515
+ token = message.content.split()[-1].strip()
516
+ try:
517
+ user = HfApi().whoami(token)
518
+ except HTTPError as e:
519
+ await message.channel.send(f"Error occured when trying to authenticate. Likely invalid API Token. {e}")
520
+ token = "abc" # reset right after we use to be safe
521
+
522
+ if user['type'] == 'org':
523
+ await message.channel.send(
524
+ "Authentication failed because you tried to authenticate with an organization's API token. Please authenticate with your User API token instead."
525
+ )
526
+ return
527
+
528
+ try:
529
+ server = bot.get_guild(879548962464493619)
530
+ #role = discord.utils.get(server.roles, name="verified")
531
+ role = server.get_role(900063512829755413)
532
+ member = server.get_member(message.author.id)
533
+ verified_date = datetime.now().strftime("%m/%d/%Y, %H:%M:%S")
534
+ likes = 0
535
+ models = 0
536
+ datasets = 0
537
+ spaces = 0
538
+ discussions = 0
539
+ papers = 0
540
+ upvotes = 0
541
+ # important!-----------------------------------------------------------------------------
542
+ # In order to add verification to a discord account, some important checks must ALL pass:
543
+ # 1. Discord account should not already have verified role.
544
+ # 2. hf_user_name should not already exist in the dataframe.
545
+ # -----> If it does, it means we are trying to link 1 HF account to multiple discord accounts. 1->many is disallowed.
546
+
547
+ # check if the member has the verified role (prevent duplicate entries in google sheet)
548
+ # if users want to change discord / HF accounts, we can deal with that manually
549
+ if role in member.roles:
550
+ await message.channel.send(f"{member} already has has the '{role}' role and is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
551
+ return
552
+
553
+ # check if hf_user_name in dataframe:
554
+ if user['name'] in global_df['hf_user_name'].values:
555
+ await message.channel.send(f"The HF account {user['name']} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
556
+ return
557
+
558
+ # check if discord_user_id in dataframe:
559
+ altered_member_id = "L" + str(member.id) + "L"
560
+ if altered_member_id in global_df['discord_user_id'].values:
561
+
562
+ hf_user_name = global_df.loc[global_df['discord_user_id'] == altered_member_id, 'hf_user_name'].iloc[0]
563
+
564
+ if pd.isnull(hf_user_name) or hf_user_name == 'n/a':
565
+ # empty (no link created yet between discord_user_id and hf_user_name) so we can update
566
+ global_df.loc[global_df['discord_user_id'] == altered_member_id, 'hf_user_name'] = user['name']
567
+ global_df.loc[global_df['discord_user_id'] == altered_member_id, 'verified_date'] = verified_date
568
+ await member.add_roles(role)
569
+ if role in member.roles:
570
+ print(f"Updated hf_user_name for id {member.id} | discord_user_name {member} | hf_user_name {user['name']}")
571
+ await message.channel.send(f"Verification successful! [{member} <---> {user['name']}] 🤗")
572
+ await lunar.send(f"Verification successful! [{member} <---> {user['name']}] 🤗")
573
+ print(f"Verification successful! [{member} <---> {user['name']}] 🤗")
574
+ return
575
+ else:
576
+ await message.channel.send(f"The Discord account {member} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
577
+ await lunar.send(f"The Discord account {member} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
578
+ print(f"The Discord account {member} is already verified! To change discord accounts or HF accounts, contact <@811235357663297546> or adam@huggingface.co")
579
+ return
580
+ except Exception as e:
581
+ print(f"verification Error: {e}")
582
+
583
+ # -----------------------------------------------------------------------------------------
584
+ # If creating new record (edge case)
585
+ xp = 10
586
+ discord_exp = "L10L"
587
+ hub_exp = "L0L"
588
+ total_exp = "L10L"
589
+ current_level = calculate_level(xp)
590
+
591
+ row_data = [altered_member_id,
592
+ member.name,
593
+ discord_exp,
594
+ current_level,
595
+ user['name'],
596
+ "L0L",
597
+ hub_exp,
598
+ total_exp,
599
+ verified_date,
600
+ likes,
601
+ models,
602
+ datasets,
603
+ spaces,
604
+ discussions,
605
+ papers,
606
+ upvotes]
607
+
608
+ global_df.loc[len(global_df.index)] = row_data
609
+ await member.add_roles(role)
610
+ if role in member.roles:
611
+ print(f"New record created for {member}")
612
+ print("------------------------------------------------------------------------")
613
+ await bot.process_commands(message)
614
+ except Exception as e:
615
+ print(f"on_message Error: {e}")
616
+
617
+
618
+ @bot.event
619
+ async def on_reaction_add(reaction, user):
620
+ try:
621
+ if user.id not in bot_ids:
622
+ print(f"adding exp from react {user}")
623
+ await asyncio.sleep(0.1)
624
+ if not isinstance(reaction.message.channel, discord.DMChannel): # can't earn exp from reacting in bot DMs, which is harder to track
625
+ await add_exp(user.id)
626
+ await asyncio.sleep(0.1)
627
+ if reaction.message.author.id != user.id: # can't earn while self-reacting, which is abuseable
628
+ await add_exp(reaction.message.author.id)
629
+ except Exception as e:
630
+ print(f"on_reaction_add Error: {e}")
631
+
632
+
633
+ def format_hyperlink(username):
634
+ return f'<a target="_blank" href="https://huggingface.co/{username}" style="color: var(--link-text-color);">{username}</a>'
635
+
636
+
637
+ @tasks.loop(minutes=1)
638
+ async def remove_huggingfolks():
639
+ try:
640
+ # remove huggingfolks
641
+ global community_global_df
642
+ global community_global_df_with_id
643
+ global community_global_df_gradio
644
+
645
+ community_global_df = global_df.copy()
646
+
647
+ guild = bot.get_guild(879548962464493619)
648
+ role = discord.utils.get(guild.roles, id=897376942817419265)
649
+ members_with_role = [member.id for member in guild.members if role in member.roles]
650
+
651
+ # remove L formatting (doesn't affect main global_df)
652
+ community_global_df['discord_user_id'] = community_global_df['discord_user_id'].str.strip('L').astype(str)
653
+
654
+ for member_id in members_with_role:
655
+ community_global_df = community_global_df[community_global_df.iloc[:, 0] != str(member_id)]
656
+
657
+ # make a copy while discord id column still exists -> use for rank in discord embeds
658
+ community_global_df_with_id = community_global_df_with_id.copy()
659
+
660
+ # reorder (switching from discord_user_name to hf_user_name)
661
+ reorder = ['discord_user_id', 'hf_user_name', 'discord_exp','discord_level', 'discord_user_name', 'hub_exp', 'total_exp', 'verified_date']
662
+ community_global_df = community_global_df[reorder]
663
+
664
+ # drop first column (discord id -> this is so we can display the important stuff in the leaderboard)
665
+ community_global_df.drop(community_global_df.columns[0], axis=1, inplace=True)
666
+ community_global_df.drop(community_global_df.columns[1], axis=1, inplace=True)
667
+ community_global_df.drop(community_global_df.columns[2], axis=1, inplace=True)
668
+ community_global_df.drop(community_global_df.columns[2], axis=1, inplace=True)
669
+ community_global_df.drop(community_global_df.columns[3], axis=1, inplace=True)
670
+ # drop rows (records) where hf_user_name does not exist (we display only verified users)
671
+ community_global_df = community_global_df.dropna(subset=['hf_user_name'])
672
+ community_global_df['total_exp'] = community_global_df['total_exp'].str.strip('L').astype(int)
673
+ community_global_df['total_exp'] = pd.to_numeric(community_global_df['total_exp'], errors='coerce').fillna(0).astype(int)
674
+ community_global_df = community_global_df.nlargest(len(community_global_df), 'total_exp')
675
+ # add profile hyperlinks to gradio demo
676
+ community_global_df_gradio = community_global_df.copy() # must be a COPY, otherwise it adds a new name for the same dataframe.
677
+ community_global_df_gradio['hf_user_name'] = community_global_df_gradio['hf_user_name'].apply(format_hyperlink)
678
+
679
+ top_num = 30
680
+ top_num_exp = community_global_df.nlargest(top_num, 'total_exp')
681
+
682
+ top_num_exp['D'] = ['🥇','🥈','🥉','','','','','','','','','','','','','','','','','','','','','','','','','','','']
683
+ top_num_rows = top_num_exp.values.tolist()
684
+
685
+ #print(top_30_rows)
686
+ channel = bot.get_channel(1197143964994773023)
687
+ message = await channel.fetch_message(1197148293164187678)
688
+
689
+ # put into message / leaderboard
690
+ new_table = tabulate(top_num_rows, headers=["Name", "Level", "Experience", "Rank"], tablefmt="plain")
691
+ await message.edit(content=f"Updated Leaderboard:\n```\n{new_table}\n```")
692
+ print("Updated discord leaderboard!")
693
+ print("------------------------------------------------------------------------")
694
+ except Exception as e:
695
+ print(f"remove_huggingfolks Error: {e}")
696
+
697
+
698
+ @bot.command(name='xp_help')
699
+ async def xp_help(ctx):
700
+ try:
701
+ help_message = "How to earn Discord / Hub exp: Post messages, react, Like, discuss, create repos and papers"
702
+ await ctx.author.send(help_message)
703
+ except Exception as e:
704
+ print(f"xp_help Error: {e}")
705
+
706
+
707
+ @bot.command()
708
+ async def count_users_with_role(ctx, role_id):
709
+ role = discord.utils.get(ctx.guild.roles, id=int(role_id))
710
+ count = sum(1 for member in ctx.guild.members if role in member.roles)
711
+ await ctx.send(f"Number of users with the role '{role.name}': {count}")
712
+
713
+
714
+ DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None)
715
+ def run_bot():
716
+ bot.run(DISCORD_TOKEN)
717
+ threading.Thread(target=run_bot).start()
718
+
719
+
720
+ def get_data():
721
+ try:
722
+ return community_global_df_gradio
723
+ except Exception as e:
724
+ print(f"get_data Error: {e}")
725
+
726
+
727
+ def get_data2():
728
+ try:
729
+ display_data = {
730
+ '🤗 Hub (+30 exp)': ['Creating Repos', 'Papers', 'Likes/Upvotes', 'Discussions'],
731
+ 'Discord (+10 exp)': ['Posting messages', 'Reacting', '', '']
732
+ }
733
+ display_df = pd.DataFrame(display_data)
734
+ return display_df
735
+ except Exception as e:
736
+ print(f"get_data2 Error: {e}")
737
+
738
+
739
+ demo = gr.Blocks()
740
+ with demo:
741
+ try:
742
+ dataframe1 = pd.read_csv("https://docs.google.com/spreadsheets/d/1C8aLqgCqLYcMiIFf-P_Aosaa03C_WLIB_UyqvjSdWg8/export?format=csv&gid=0") # required for timing
743
+ print(dataframe1)
744
+
745
+ level_counts = dataframe1['discord_level'].value_counts().sort_index()
746
+ dataframe2 = pd.DataFrame({
747
+ 'Levels': level_counts.index,
748
+ 'Members': level_counts.values
749
+ })
750
+ print(dataframe2)
751
+
752
+ TITLE = """<h1 align="center" id="space-title">🤗 Hugging Face Level Leaderboard</h1>"""
753
+ gr.HTML(TITLE)
754
+ with gr.Tabs(elem_classes="tab-buttons") as tabs:
755
+ with gr.TabItem("🏅 Level leaderboard", elem_id="level-table", id=0):
756
+ #gr.Markdown("# 📈 Experience Leaderboard")
757
+ with gr.Row():
758
+ with gr.Column():
759
+ gr.DataFrame(get_data, every=5, height=500, datatype=["markdown"], interactive=False, col_count=(3, "fixed"), column_widths=["100px","100px","100px"])
760
+
761
+ with gr.Column():
762
+ gr.BarPlot(
763
+ value=dataframe2,
764
+ x="Levels",
765
+ y="Members",
766
+ title="Level Distribution",
767
+ height=450,
768
+ width=450,
769
+ interactive=False
770
+ )
771
+ with gr.Row():
772
+ gr.Markdown("# 📈 How to earn Experience!")
773
+ with gr.Row():
774
+ gr.DataFrame(get_data2, every=5, interactive=False)
775
+ #with gr.TabItem("📈 Members of the Week", elem_id="week-table", id=1):
776
+
777
+ #with gr.TabItem("📈 Hub-only leaderboard", elem_id="hub-table", id=2):
778
+ except Exception as e:
779
+ print(f"gradio demo Error: {e}")
780
  demo.queue().launch()
requirements.txt CHANGED
@@ -1,9 +1,10 @@
1
- discord.py
2
- gradio
3
- gspread==5.12.0
4
- huggingface_hub
5
- tabulate
6
- apscheduler
7
- gspread-dataframe
8
- gspread-formatting
9
- numpy
 
 
1
+ discord.py
2
+ gradio
3
+ gspread==5.12.0
4
+ huggingface_hub
5
+ tabulate
6
+ apscheduler
7
+ gspread-dataframe
8
+ gspread-formatting
9
+ numpy
10
+ pandas
startup.py ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ import test
2
+ import app
test.py ADDED
@@ -0,0 +1,96 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import unittest
2
+ from unittest.mock import patch, MagicMock
3
+ import pandas as pd
4
+ import requests
5
+ import os
6
+ import asyncio
7
+ from app import (
8
+ calculate_level, calculate_xp, add_exp, update_google_sheet,
9
+ update_hub_stats, on_ready, DISCORD_TOKEN, bot, global_df, test_merge
10
+ )
11
+
12
+ class TestAppFunctions(unittest.TestCase):
13
+
14
+ @classmethod
15
+ def setUpClass(cls):
16
+ # Set up any state needed before tests are run
17
+ cls.test_member_id = 123456789
18
+ cls.test_xp = 1000
19
+ cls.test_level = 10
20
+
21
+ def test_calculate_level(self):
22
+ level = calculate_level(self.test_xp)
23
+ self.assertEqual(level, int(self.test_xp ** (1.0 / 3.0)))
24
+
25
+ def test_calculate_xp(self):
26
+ xp = calculate_xp(self.test_level)
27
+ self.assertEqual(xp, int(self.test_level ** 3))
28
+
29
+ @patch('app.global_df', new_callable=pd.DataFrame)
30
+ @patch('app.update_google_sheet')
31
+ def test_add_exp(self, mock_update_google_sheet, mock_global_df):
32
+ # Mock the bot and guild setup
33
+ bot.get_guild.return_value.get_member.return_value = MagicMock()
34
+
35
+ # Add mock data to global_df
36
+ data = {'discord_user_id': ['L123456789L'], 'discord_exp': ['L1000L'], 'total_exp': ['L2000L'], 'hub_exp': ['L1000L']}
37
+ mock_global_df.return_value = pd.DataFrame(data)
38
+
39
+ # Run add_exp and assert it does not raise exceptions
40
+ try:
41
+ asyncio.run(add_exp(self.test_member_id))
42
+ success = True
43
+ except Exception as e:
44
+ success = False
45
+ self.assertTrue(success)
46
+
47
+ @patch('requests.get')
48
+ def test_update_hub_stats(self, mock_get):
49
+ # Set up mock response for requests.get
50
+ mock_response = MagicMock()
51
+ mock_response.status_code = 200
52
+ mock_response.json.return_value = {
53
+ "numLikes": 10,
54
+ "numModels": 5,
55
+ "numDatasets": 3,
56
+ "numSpaces": 2,
57
+ "numDiscussions": 7,
58
+ "numPapers": 1,
59
+ "numUpvotes": 4
60
+ }
61
+ mock_get.return_value = mock_response
62
+
63
+ # Add mock data to global_df
64
+ data = {'hf_user_name': ['test_user'], 'discord_exp': ['L1000L'], 'total_exp': ['L2000L'], 'hub_exp': ['L1000L']}
65
+ global_df = pd.DataFrame(data)
66
+
67
+ try:
68
+ update_hub_stats()
69
+ success = True
70
+ except Exception as e:
71
+ success = False
72
+ self.assertTrue(success)
73
+
74
+ def test_on_ready(self):
75
+ # Test the real bot's on_ready method
76
+ async def run_on_ready():
77
+ await on_ready()
78
+
79
+ try:
80
+ asyncio.run(run_on_ready())
81
+ success = True
82
+ except Exception as e:
83
+ success = False
84
+ self.assertTrue(success)
85
+
86
+ def test_update_google_sheet(self):
87
+ # Mock gspread and related functions
88
+ with patch('app.set_with_dataframe') as mock_set_with_dataframe:
89
+ update_google_sheet()
90
+ mock_set_with_dataframe.assert_called()
91
+
92
+ def test_env_key_exists(self):
93
+ self.assertIn('KEY', os.environ, "The 'KEY' environment variable is not set.")
94
+
95
+ if __name__ == '__main__':
96
+ unittest.main()