Spaces:
Running on CPU Upgrade

lunarflu HF Staff commited on
Commit
4e807ea
Β·
verified Β·
1 Parent(s): 3a0c15f

test adjust

Browse files
Files changed (1) hide show
  1. app.py +51 -787
app.py CHANGED
@@ -45,31 +45,11 @@ intents = discord.Intents.all()
45
  bot = commands.Bot(command_prefix='!', intents=intents)
46
 
47
  GRADIO_APP_URL = "https://huggingface.co/spaces/discord-community/LevelBot"
48
- # Dictionary to store user IDs and their corresponding unique strings
49
- user_tokens = {}
50
 
51
 
52
- """"""
53
- XP_PER_MESSAGE = 10 # 100k messages = 1M exp = lvl 100
54
- """"""
55
- service_account = json.loads(os.environ.get('KEY'))
56
- file_path = 'service_account.json'
57
- with open(file_path, 'w') as json_file:
58
- json.dump(service_account, json_file)
59
- gspread_bot = gspread.service_account(filename='service_account.json')
60
- worksheet = gspread_bot.open("levelbot").sheet1
61
- test_merge_worksheet = gspread_bot.open("test_merge").sheet1
62
  """"""
63
  bot_ids = [1136614989411655780, 1166392942387265536, 1158038249835610123, 1130774761031610388, 1155489509518098565, 1155169841276260546, 1152238037355474964, 1154395078735953930]
64
  """"""
65
- api = HfApi()
66
- """"""
67
- global_df = pd.DataFrame()
68
- print(type(global_df))
69
- community_global_df = pd.DataFrame()
70
- community_global_df_with_id = pd.DataFrame()
71
- community_global_df_gradio = pd.DataFrame()
72
- test_merge = pd.read_csv("https://docs.google.com/spreadsheets/d/1C8aLqgCqLYcMiIFf-P_Aosaa03C_WLIB_UyqvjSdWg8/export?format=csv&gid=0")
73
 
74
  hf_token = os.environ.get("HF_TOKEN")
75
 
@@ -81,26 +61,28 @@ class DMButton(Button):
81
  async def callback(self, interaction: discord.Interaction):
82
  # await interaction.user.send(self.message) # this is for DMs, but users may have DMs disabled
83
  user_id = interaction.user.id
84
- if int(user_id) in user_tokens:
85
- del user_tokens[int(user_id)] # always delete all past tokens for a given user when creating new link
86
- unique_string = generate_unique_string()
87
- user_tokens[user_id] = unique_string
88
- unique_link = f"<{GRADIO_APP_URL}?user_id={user_id}&token={unique_string}>"
 
 
 
 
 
89
  message = f"Login link generated! To complete the verification process:\n- 1 Visit this link,\n- 2 click the 'πŸ€—Sign in with Hugging Face' button\n\n{unique_link}"
90
  await interaction.response.send_message(message, ephemeral=True)
91
 
92
 
93
- def generate_unique_string(length=6):
94
- return secrets.token_hex(length // 2)
95
 
96
 
97
  @bot.event
98
  async def on_ready():
99
  try:
100
- global global_df
101
- await asyncio.sleep(1.1)
102
  print(f'Logged in as {bot.user.name}')
103
- print(f"XP_PER_MESSAGE: {XP_PER_MESSAGE}")
104
 
105
  guild = bot.get_guild(879548962464493619)
106
  await guild.chunk() # get all users into bot cache
@@ -121,541 +103,23 @@ async def on_ready():
121
  print(f"Failed to fetch message with ID 1271145797433557023: {e}")
122
 
123
 
124
- """import data from google sheets -> HF Space df (doesn't make API call this way, as it's read-only)"""
125
- global_df = test_merge
126
- print(f"csv successfully retrieved: \n {global_df}")
127
-
128
-
129
- # updates both leaderboards
130
- remove_huggingfolks.start()
131
-
132
- print("------------------------------------------------------------------------")
133
- except Exception as e:
134
- print(f"on_ready Error: {e}")
135
-
136
-
137
- def update_google_sheet():
138
- """save data from HF Space -> google sheets (makes 1 API call)"""
139
- start_time = time.time()
140
- timeout = 300
141
- try:
142
- print("Updating google sheets...")
143
- print("------------------------------------------------------------------------")
144
- name = "test_merge_worksheet"
145
- set_with_dataframe(test_merge_worksheet, global_df)
146
- update_hf_dataset(global_df) # starting to migrate away from google sheets and towards HF datasets
147
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
148
- print("------------------------------------------------------------------------")
149
- print(f"Google sheet {name} {test_merge_worksheet} successfully updated at {timestamp}! \n{global_df}")
150
- print("------------------------------------------------------------------------")
151
- if time.time() - start_time > timeout:
152
- print(f"Job exceeded timeout of {timeout} seconds.")
153
- except Exception as e:
154
- print(f"update_google_sheet Error: {e}")
155
-
156
-
157
- # starting to migrate away from google sheets and towards HF datasets
158
- def update_hf_dataset(df, repo_id="lunarflu/levelbot-data", filename="levelbot-data.csv"):
159
- try:
160
- hf_token = os.environ.get("HF_TOKEN")
161
- api = HfApi(token=hf_token)
162
-
163
- with tempfile.NamedTemporaryFile(mode='w', suffix=".csv", delete=False) as tmp:
164
- df.to_csv(tmp.name, index=False)
165
- api.upload_file(
166
- path_or_fileobj=tmp.name,
167
- path_in_repo=filename,
168
- repo_id=repo_id,
169
- repo_type="dataset",
170
- token=hf_token,
171
- commit_message="Update leaderboard CSV"
172
- )
173
- print(f"βœ… Hugging Face dataset `{repo_id}/{filename}` updated successfully.")
174
- except Exception as e:
175
- print(f"❌ update_hf_dataset Error: {e}")
176
-
177
-
178
- #@tasks.loop(minutes=1) tasks.loop leads to heartbeat blocked issues (merging calculations too much with normal discord bot functions)
179
- def update_hub_stats():
180
- try:
181
- global global_df
182
- print("Updating hub stats...")
183
- print("------------------------------------------------------------------------")
184
-
185
- for index, row in global_df.iterrows():
186
- user = row['hf_user_name']
187
- if pd.notna(user):
188
- #print(f"user: {user}")
189
- url = f"https://huggingface.co/api/users/{user}/overview"
190
- #print(f"url: {url}")
191
- response = requests.get(url)
192
- #print(f"response: {response}")
193
- if response.status_code == 200:
194
- data = response.json()
195
- #print(f"data: {data}")
196
- likes = data["numLikes"]
197
- models = data["numModels"]
198
- datasets = data["numDatasets"]
199
- spaces = data["numSpaces"]
200
- discussions = data["numDiscussions"]
201
- papers = data["numPapers"]
202
- upvotes = data["numUpvotes"]
203
-
204
- # recalculate level as well (no longer only depends on discord activity)
205
- try:
206
- global_df.loc[index, 'likes'] = likes
207
- global_df.loc[index, 'models'] = models
208
- global_df.loc[index, 'datasets'] = datasets
209
- global_df.loc[index, 'spaces'] = spaces
210
- global_df.loc[index, 'discussions'] = discussions
211
- global_df.loc[index, 'papers'] = papers
212
- global_df.loc[index, 'upvotes'] = upvotes
213
-
214
- total_hub_activity = likes + models + datasets + spaces + discussions + papers + upvotes
215
- total_hub_exp = total_hub_activity * 20 #2x better than discord
216
- total_hub_exp_string = "L" + str(total_hub_exp) + "L"
217
-
218
- # hub exp
219
- global_df.loc[index, 'hub_exp'] = total_hub_exp_string
220
-
221
- # total exp (discord + hub)
222
- discord_exp = row['discord_exp']
223
- discord_exp_int = discord_exp.strip('L')
224
- total_exp = int(discord_exp_int) + int(total_hub_exp)
225
- total_exp_string = "L" + str(total_exp) + "L"
226
- global_df.loc[index, 'total_exp'] = total_exp_string
227
-
228
- # level
229
- level = calculate_level(total_exp)
230
- global_df.loc[index, 'discord_level'] = level
231
-
232
- except Exception as e:
233
- print(f"{e} error updating the dataframe")
234
-
235
-
236
- except Exception as e:
237
- print(f"Failed to parse data for user {user}. {e}")
238
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
239
- print("------------------------------------------------------------------------")
240
- print(f"Hub stats successfully updated at {timestamp}! \n{global_df}")
241
- print("------------------------------------------------------------------------")
242
-
243
-
244
- thread_pool_executor = ThreadPoolExecutor(max_workers=2)
245
- asyncio_executor = AsyncIOExecutor()
246
- scheduler = BackgroundScheduler(executors={
247
- 'default': thread_pool_executor
248
- })
249
- scheduler.add_job(update_google_sheet, trigger='interval', minutes=1, max_instances=5, executor='default')
250
- scheduler.add_job(update_hub_stats, trigger='interval', minutes=1.5, max_instances=5, executor='default')
251
- scheduler.start()
252
- #asyncio.get_event_loop().run_forever()
253
-
254
-
255
- def calculate_level(xp):
256
- return int(xp ** (1.0 / 3.0))
257
-
258
-
259
- def calculate_xp(level):
260
- return (int(level ** 3))
261
-
262
-
263
- async def add_exp(member_id):
264
- try:
265
- """Uses member_id to create new record or update old one [member_id_column] ... [member_exp_column]"""
266
- await asyncio.sleep(0.1)
267
- global global_df
268
-
269
- guild = bot.get_guild(879548962464493619)
270
- member = guild.get_member(member_id) # bot.get_user == User, guild.get_member == Member (not the same thing!)
271
-
272
- lvl1 = guild.get_role(1171861537699397733)
273
- lvl2 = guild.get_role(1171861595115245699)
274
- lvl3 = guild.get_role(1171861626715115591)
275
- lvl4 = guild.get_role(1171861657975259206)
276
- lvl5 = guild.get_role(1171861686580412497)
277
- lvl6 = guild.get_role(1171861900301172736)
278
- lvl7 = guild.get_role(1171861936258941018)
279
- lvl8 = guild.get_role(1171861968597024868)
280
- lvl9 = guild.get_role(1171862009982242836)
281
- lvl10 = guild.get_role(1164188093713223721)
282
-
283
- lvl11 = guild.get_role(1171524944354607104)
284
- lvl12 = guild.get_role(1171524990257082458)
285
- lvl13 = guild.get_role(1171525021928263791)
286
- lvl14 = guild.get_role(1171525062201966724)
287
- lvl15 = guild.get_role(1171525098465918996)
288
- lvl16 = guild.get_role(1176826165546201099)
289
- lvl17 = guild.get_role(1176826221301092392)
290
- lvl18 = guild.get_role(1176826260643659776)
291
- lvl19 = guild.get_role(1176826288816791693)
292
- lvl20 = guild.get_role(1176826319447801896)
293
-
294
- lvl21 = guild.get_role(1195030831174008902)
295
- lvl22 = guild.get_role(1195030883351150592)
296
- lvl23 = guild.get_role(1196055555006009445)
297
- lvl24 = guild.get_role(1196055640917938216)
298
- lvl25 = guild.get_role(1196055712506318869)
299
- lvl26 = guild.get_role(1196055775924195378)
300
- lvl27 = guild.get_role(1196055837018435664)
301
- lvl28 = guild.get_role(1196055908267081849)
302
- lvl29 = guild.get_role(1196055970804150352)
303
- lvl30 = guild.get_role(1196056027720847380)
304
-
305
- lvl31 = guild.get_role(1206542603261186078)
306
- lvl32 = guild.get_role(1206542673549205514)
307
- lvl33 = guild.get_role(1206542690939048007)
308
- lvl34 = guild.get_role(1206542707862806568)
309
- lvl35 = guild.get_role(1206542723633512468)
310
- lvl36 = guild.get_role(1206542738728681485)
311
- lvl37 = guild.get_role(1206542754625101866)
312
- lvl38 = guild.get_role(1206542771314364416)
313
- lvl39 = guild.get_role(1206542785973321758)
314
- lvl40 = guild.get_role(1206542802155208725)
315
-
316
- lvl41 = guild.get_role(1206568953221218354)
317
- lvl42 = guild.get_role(1206568979393413150)
318
- lvl43 = guild.get_role(1206568997374394368)
319
- lvl44 = guild.get_role(1206569014747463681)
320
- lvl45 = guild.get_role(1206569031650385921)
321
- lvl46 = guild.get_role(1206569047207182356)
322
- lvl47 = guild.get_role(1206569062851805254)
323
- lvl48 = guild.get_role(1206569077112315984)
324
- lvl49 = guild.get_role(1206569091826057306)
325
- lvl50 = guild.get_role(1206569107118493757)
326
-
327
- lvls = {
328
- 1: lvl1, 2: lvl2, 3: lvl3, 4: lvl4, 5: lvl5, 6: lvl6, 7: lvl7, 8: lvl8, 9: lvl9, 10: lvl10,
329
- 11: lvl11, 12: lvl12, 13: lvl13, 14: lvl14, 15: lvl15, 16: lvl16, 17: lvl17, 18: lvl18, 19: lvl19, 20: lvl20,
330
- 21: lvl21, 22: lvl22, 23: lvl23, 24: lvl24, 25: lvl25, 26: lvl26, 27: lvl27, 28: lvl28, 29: lvl29, 30: lvl30,
331
- 31: lvl31, 32: lvl32, 33: lvl33, 34: lvl34, 35: lvl35, 36: lvl36, 37: lvl37, 38: lvl38, 39: lvl39, 40: lvl40,
332
- 41: lvl41, 42: lvl42, 43: lvl43, 44: lvl44, 45: lvl45, 46: lvl46, 47: lvl47, 48: lvl48, 49: lvl49, 50: lvl50,
333
- }
334
-
335
- member_found = False
336
-
337
- print(f"Searching for member_id {member_id} in dataframe...")
338
- # discord_user_id column
339
- # iterate over items of first column (discord_user_id)
340
- for index, cell_value in global_df.iloc[:, 0].items():
341
- # tldr; set_as_dataframe forces scientific notation which corrupts discord_user_id data.
342
- # set_as_dataframe is still highly efficient (1 API call), so we format numerical data as strings,
343
- # which results in efficient google sheet updating + data integrity
344
- cell_value = str(cell_value)
345
- if cell_value.startswith("L") and cell_value.endswith("L"):
346
- cell_value_clipped = cell_value[1:-1]
347
- if cell_value_clipped == str(member_id): # str(member_id) needed, it is int by default
348
- print("test4")
349
- # if found, update that row...
350
- member_found = True
351
- print(f"Record for {member} found at row {index + 1}, column 1")
352
-
353
- # increment the old experience value (better not to replace outright)
354
- old_xp = global_df.loc[index, 'discord_exp']
355
-
356
- # remove L (write, so we replace)
357
- old_xp = str(old_xp)
358
- if old_xp.startswith("L") and old_xp.endswith("L"):
359
- old_xp = old_xp[1:-1]
360
-
361
- # str -> int temporarily for adding
362
- new_xp = int(old_xp) + XP_PER_MESSAGE
363
-
364
- total_exp = global_df.loc[index, 'total_exp']
365
- hub_xp = global_df.loc[index, 'hub_exp']
366
-
367
- total_exp = str(total_exp)
368
- hub_xp = str(hub_xp)
369
-
370
- if total_exp.startswith("L") and total_exp.endswith("L"):
371
- total_exp = total_exp[1:-1]
372
- if hub_xp.startswith("L") and hub_xp.endswith("L"):
373
- hub_xp = hub_xp[1:-1]
374
-
375
- # set old level; use this for more accurate logging and jumping multiple levels at once (for example, verifying)
376
- old_total_xp = int(total_exp)
377
- #old_level = calculate_level(old_total_xp)
378
-
379
- # check if hub exp not empty
380
- if hub_xp.strip():
381
- total_exp = int(new_xp) + int(hub_xp)
382
- else:
383
- total_exp = int(new_xp)
384
-
385
- # total v
386
- current_level = calculate_level(total_exp)
387
-
388
- # convert back to string + google sheet proofing
389
- new_xp = str(new_xp)
390
- if not new_xp.startswith("L") and not new_xp.endswith("L"):
391
- new_xp = "L" + str(new_xp) + "L"
392
- global_df.loc[index, 'discord_exp'] = new_xp # do not change column name
393
-
394
- # after
395
- total_exp = str(total_exp)
396
- if not total_exp.startswith("L") and not total_exp.endswith("L"):
397
- total_exp = "L" + str(total_exp) + "L"
398
-
399
- # add back to dataframe in memory after checking redundantly;
400
- if total_exp.startswith("L") and total_exp.endswith("L"):
401
- print("test5")
402
- global_df.loc[index, 'total_exp'] = total_exp # do not change column name
403
- print(f"Record for {member} updated from {old_total_xp} to {global_df.loc[index, 'total_exp']} (+{XP_PER_MESSAGE}) ")
404
-
405
- # level up
406
- verified_role = guild.get_role(900063512829755413)
407
-
408
- print(f"Current_level for {member}: {current_level}")
409
- if current_level >= 2 and current_level <=50:
410
- print("test6")
411
- current_role = lvls[current_level]
412
- if current_role not in member.roles: # if we need to level up / update role
413
- print("test7")
414
- # finding leaderboard rank + excluding huggingfolks (still need exclusion)
415
- try:
416
- print("Calculating rank...")
417
- copy_df = global_df.copy()
418
- copy_df['discord_user_id'] = copy_df['discord_user_id'].str.strip('L').astype(str)
419
- copy_df['total_exp'] = copy_df['total_exp'].str.strip('L').astype(int)
420
- row = copy_df[copy_df['discord_user_id'] == str(member_id)]
421
- target_exp = row['total_exp'].values[0]
422
- rank = (copy_df['total_exp'] > target_exp).sum() + 1
423
- print(f"The rank for {member} based on total_exp is: {rank}")
424
- except Exception as e:
425
- print(f"Discord ID {member} {member_id} not found in the DataFrame. {e}")
426
- rank = "πŸ€—"
427
-
428
- # if level 3 -> then send embed, remove some exp
429
- if current_level >= 3: # could change to 4 maybe
430
- if verified_role not in member.roles:
431
-
432
- # L12345L -> `12345` -> 12345
433
- total_exp = total_exp[1:-1]
434
- total_exp = int(total_exp)
435
-
436
- if total_exp % 30 == 0: # staggers messages so we don't send one every time exp is earned
437
- # claim exp (-30 for level 3, but +100 as bonus exp. This scales infinitely until the member verifies,
438
- # so they can continue earning exp, it just won't translate to levels and the leaderboard.
439
- # This way they can claim at any time and get a big boost in levels!
440
- claim_exp = total_exp + 70
441
-
442
- # send embed
443
- embed = Embed(color=Color.red())
444
- embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
445
- embed.title = f"⚠️Your account is not Verified! Unable to level up `πŸš€` -> `{current_level}` ❌"
446
- 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!'
447
- embed.description = f"{msg}"
448
- verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
449
- embed.add_field(name="Verify Here:", value=verification_link, inline=True)
450
- u_1 = "πŸ‘‘ Earn exp for activity on Discord and HF and climb the ⁠leaderboard !"
451
- u_2 = "🌎 Feature your content in weekly news and increase its visibility!"
452
- u_3 = "πŸš€ Early access to Beta features!"
453
- u_4 = "πŸ›‘οΈ Secure your progress, and restore if needed!"
454
- embed.add_field(name="You can Unlock:", value=f"{u_1}\n{u_2}\n{u_3}\n{u_4}", inline=True)
455
- embed.set_image(url='https://cdn.discordapp.com/attachments/1150399343912833024/1205537451242688573/download_1.png?ex=65d8bb3e&is=65c6463e&hm=042fe7dd3521887db0bd48eeb846de1cc7c75194f9e95215c23512ff61ea3475&')
456
-
457
- lunar = bot.get_user(811235357663297546)
458
- await member.send(embed=embed)
459
- await lunar.send(embed=embed)
460
- print("Sent verification cap embed to {member}")
461
- print("------------------------------------------------------------------------")
462
- return
463
-
464
- # increment the old level value (better to replace outright)
465
- # only increment level column if you are lvl2 or 3+ with verified role (this may make some members not appear)
466
- global_df.loc[index, 'discord_level'] = current_level # do not change column name
467
-
468
- # remove all level roles then add new role
469
- current_level_roles = [role for level, role in lvls.items() if role in member.roles]
470
- if current_level_roles:
471
- print(f"current_level_roles for {member}: {current_level_roles}")
472
- for removable_role in current_level_roles:
473
- await member.remove_roles(removable_role)
474
- print(f"Removed {removable_role} from {member}")
475
- removable_role_name = removable_role.name
476
- else:
477
- removable_role_name = "πŸš€"
478
- await member.add_roles(current_role)
479
- print(f"Level Up! Gave {member} {current_role}")
480
-
481
- if current_level == 2: # special embed with opt-out instructions for discord newcomers
482
- embed = Embed(color=Color.blue())
483
- embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
484
- embed.title = f"Level Up! `{removable_role_name}` -> `{current_level}`"
485
- 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!'
486
- embed.description = f"{msg}."
487
- embed.add_field(name="Leaderboard Ranking:", value=f"πŸ‘‘ **{rank}**\n\nhttps://discord.com/channels/879548962464493619/1197143964994773023", inline=True)
488
- msg3 = "- Posting\n- Reacting / being reacted to\n- Being active on the Hugging Face Hub (verify to link your Hub + Discord accounts!)"
489
- embed.add_field(name="How to Level Up:", value=msg3, inline=True)
490
- verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
491
- embed.add_field(name="Verify Here:", value=verification_link, inline=True)
492
- lunar = bot.get_user(811235357663297546)
493
- await member.send(embed=embed)
494
- await lunar.send(embed=embed)
495
- print(f"Sent levelup embed to {member}")
496
- return
497
-
498
- if current_role in member.roles:
499
- # send embed
500
- embed = Embed(color=Color.blue())
501
- embed.set_author(name=f"{member}", icon_url=member.avatar.url if member.avatar else bot.user.avatar.url)
502
- embed.title = f"Level Up! `{removable_role_name}` -> `{current_level}`"
503
- msg = f'πŸ€— Congrats {member}! You just leveled up in the Hugging Face Discord server'
504
- embed.description = f"{msg}."
505
- embed.add_field(name="Leaderboard Ranking:", value=f"πŸ‘‘ **{rank}**\n\nhttps://discord.com/channels/879548962464493619/1197143964994773023", inline=True)
506
- msg3 = "- Posting\n- Reacting / being reacted to\n- Being active on the Hugging Face Hub (verify to link your Hub + Discord accounts!)"
507
- embed.add_field(name="How to Level Up:", value=msg3, inline=True)
508
- verification_link = "https://discord.com/channels/879548962464493619/900125909984624713"
509
- embed.add_field(name="Verify Here:", value=verification_link, inline=True)
510
-
511
- lunar = bot.get_user(811235357663297546)
512
- await member.send(embed=embed)
513
- await lunar.send(embed=embed)
514
- print(f"Sent levelup embed to {member}")
515
- print("------------------------------------------------------------------------")
516
- if not member_found: # this only checks if discord_user_id (with L) is present in discord_user_id column,
517
- # if not, create new record
518
- print(f"Creating new record for {member}")
519
-
520
- xp = 10 # define somewhere else?
521
- current_level = calculate_level(xp)
522
- xp = str(xp)
523
- if not xp.startswith("L") and not xp.endswith("L"):
524
- xp = "L" + str(xp) + "L"
525
- member_id = str(member_id)
526
- if not member_id.startswith("L") and not member_id.endswith("L"):
527
- member_id = "L" + str(member_id) + "L"
528
- member_name = str(member.name)
529
- hf_user_name = "n/a"
530
- hub_exp = "L0L"
531
- total_exp = xp
532
- verified_date = "n/a"
533
-
534
- # need to initialize these when creating new record
535
- likes = 0
536
- models = 0
537
- datasets = 0
538
- spaces = 0
539
- discussions = 0
540
- papers = 0
541
- upvotes = 0
542
-
543
- 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]
544
- global_df.loc[len(global_df.index)] = row_data
545
-
546
- print("------------------------------------------------------------------------")
547
- except Exception as e:
548
- print(f"add_exp Error: {e}")
549
-
550
-
551
- @bot.event
552
- async def on_message(message):
553
- org_link = "https://huggingface.co/organizations/discord-community/share/wPKRAHYbAlaEaCxUxcqVyaaaeZcYagDvqc"
554
- invite_message = "Click to join our community org on the HF Hub!"
555
- try:
556
- if message.author.id not in bot_ids: # could change to if author does not have bot role (roleid)
557
- print(f"adding exp from message {message.author}")
558
- await asyncio.sleep(0.1)
559
- await add_exp(message.author.id)
560
-
561
- # add check for verification
562
- if message.content.find("!help") != -1:
563
- await message.channel.send(
564
- "To verify your πŸ€— account, generate a login link in the verification channel! "
565
- )
566
- await bot.process_commands(message)
567
- except Exception as e:
568
- print(f"on_message Error: {e}")
569
 
570
-
571
- @bot.event
572
- async def on_reaction_add(reaction, user):
573
- try:
574
- if user.id not in bot_ids:
575
- print(f"adding exp from react {user}")
576
- await asyncio.sleep(0.1)
577
- if not isinstance(reaction.message.channel, discord.DMChannel) and reaction.message.author.id != user.id: # can't earn exp from reacting in bot DMs, which is harder to track, and can't earn while self-reacting, which is abuseable
578
- await add_exp(reaction.message.author.id)
579
- await asyncio.sleep(0.1)
580
-
581
- except Exception as e:
582
- print(f"on_reaction_add Error: {e}")
583
-
584
-
585
- def format_hyperlink(username):
586
- return f'<a target="_blank" href="https://huggingface.co/{username}" style="color: var(--link-text-color);">{username}</a>'
587
-
588
-
589
-
590
- @tasks.loop(minutes=10)
591
- async def remove_huggingfolks():
592
- try:
593
- guild = bot.get_guild(879548962464493619)
594
- role = discord.utils.get(guild.roles, id=897376942817419265)
595
- await guild.chunk()
596
- members_with_role = [member.id for member in guild.members if role in member.roles]
597
 
598
- top_num_rows = await asyncio.to_thread(_remove_huggingfolks_sync, members_with_role)
599
 
600
- channel = bot.get_channel(1197143964994773023)
601
- message = await channel.fetch_message(1197148293164187678)
602
 
603
- new_table = tabulate(top_num_rows, headers=["Name", "Level", "Experience", "Rank"], tablefmt="plain")
604
- await message.edit(content=f"Updated Leaderboard:\n```\n{new_table}\n```")
605
- print("Updated discord leaderboard!")
606
  print("------------------------------------------------------------------------")
607
  except Exception as e:
608
- print(f"remove_huggingfolks Error: {e}")
609
-
610
-
611
- def _remove_huggingfolks_sync(members_with_role):
612
- global community_global_df
613
- global community_global_df_with_id
614
- global community_global_df_gradio
615
-
616
-
617
- community_global_df = global_df.copy()
618
 
619
- community_global_df['discord_user_id'] = community_global_df['discord_user_id'].str.strip('L').astype(str)
620
 
621
- for member_id in members_with_role:
622
- community_global_df = community_global_df[community_global_df['discord_user_id'] != str(member_id)]
623
 
624
- community_global_df_with_id = community_global_df.copy()
625
 
626
- reorder = ['discord_user_id', 'hf_user_name', 'discord_exp', 'discord_level', 'discord_user_name', 'hub_exp', 'total_exp', 'verified_date']
627
- community_global_df = community_global_df[reorder]
628
 
629
- community_global_df.drop(community_global_df.columns[0], axis=1, inplace=True)
630
- community_global_df.drop(community_global_df.columns[1], axis=1, inplace=True)
631
- community_global_df.drop(community_global_df.columns[2], axis=1, inplace=True)
632
- community_global_df.drop(community_global_df.columns[2], axis=1, inplace=True)
633
- community_global_df.drop(community_global_df.columns[3], axis=1, inplace=True)
634
-
635
- # drop rows (records) where hf_user_name does not exist (we display only verified users)
636
- community_global_df = community_global_df.dropna(subset=['hf_user_name'])
637
- community_global_df['total_exp'] = community_global_df['total_exp'].str.strip('L').astype(int)
638
- community_global_df['total_exp'] = pd.to_numeric(community_global_df['total_exp'], errors='coerce').fillna(0).astype(int)
639
- community_global_df = community_global_df.nlargest(len(community_global_df), 'total_exp')
640
 
641
- community_global_df_gradio = community_global_df.copy()
642
- community_global_df_gradio['hf_user_name'] = community_global_df_gradio['hf_user_name'].apply(format_hyperlink)
643
 
644
- top_num = 30
645
- top_num_exp = community_global_df.head(top_num).copy()
646
- top_num_exp['Rank'] = ['πŸ₯‡', 'πŸ₯ˆ', 'πŸ₯‰'] + [''] * (top_num - 3)
647
- top_num_rows = top_num_exp.values.tolist()
648
- return top_num_rows
649
 
650
 
651
 
652
- @bot.command(name='xp_help')
653
- async def xp_help(ctx):
654
- try:
655
- help_message = "How to earn Discord / Hub exp: Post messages, react, Like, discuss, create repos and papers"
656
- await ctx.author.send(help_message)
657
- except Exception as e:
658
- print(f"xp_help Error: {e}")
659
 
660
 
661
  DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None)
@@ -664,201 +128,11 @@ def run_bot():
664
  threading.Thread(target=run_bot).start()
665
 
666
 
667
- def get_data():
668
- try:
669
- return community_global_df_gradio
670
- except Exception as e:
671
- print(f"get_data Error: {e}")
672
-
673
-
674
- def get_data2():
675
- try:
676
- display_data = {
677
- 'πŸ€— Hub (+30 exp)': ['Creating Repos', 'Papers', 'Likes/Upvotes', 'Discussions'],
678
- 'Discord (+10 exp)': ['Posting messages', 'Reacting', '', '']
679
- }
680
- display_df = pd.DataFrame(display_data)
681
- return display_df
682
- except Exception as e:
683
- print(f"get_data2 Error: {e}")
684
-
685
-
686
- @bot.event
687
- async def on_member_join(member):
688
- try:
689
- # Check if the member already has a record
690
- global global_df
691
- member_id = f"L{member.id}L" # Ensure consistent formatting with the database
692
- if member_id not in global_df['discord_user_id'].values:
693
- # Create a new row for the user
694
- print(f"Creating new record for {member}")
695
- xp = 0 # Start with zero XP
696
- current_level = calculate_level(xp)
697
- xp = f"L{xp}L"
698
- member_name = member.name
699
- hf_user_name = "n/a"
700
- hub_exp = "L0L"
701
- total_exp = xp
702
- verified_date = "n/a"
703
-
704
- # Initialize additional columns for the new row
705
- likes = 0
706
- models = 0
707
- datasets = 0
708
- spaces = 0
709
- discussions = 0
710
- papers = 0
711
- upvotes = 0
712
-
713
- row_data = [
714
- member_id, member_name, xp, current_level, hf_user_name, hub_exp,
715
- total_exp, verified_date, likes, models, datasets, spaces, discussions,
716
- papers, upvotes
717
- ]
718
- global_df.loc[len(global_df.index)] = row_data # Add the new row
719
-
720
- print(f"Added new user {member.name} with ID {member.id} to the database.")
721
- else:
722
- print(f"User {member.name} with ID {member.id} already exists in the database.")
723
- except Exception as e:
724
- print(f"Error adding new member {member}: {e}")
725
-
726
 
727
  #-------------------------------------------------------------------------------------------------------------------------------
728
- # year->get all dates
729
- def get_dates_in_year(year):
730
- start_date = datetime(year, 1, 1)
731
- end_date = datetime(year + 1, 1, 1)
732
- delta = end_date - start_date
733
- return [start_date + timedelta(days=i) for i in range(delta.days)]
734
-
735
- # HF API->get activity
736
- def get_user_likes(username):
737
- url = f"https://huggingface.co/api/users/{username}/likes"
738
- response = requests.get(url)
739
- if response.status_code == 200:
740
- return response.json()
741
- else:
742
- print(f"Failed to fetch data: {response.status_code}")
743
- return []
744
-
745
- # data->get dates
746
- def parse_activity_dates(activity_data):
747
- activity_dates = []
748
- for item in activity_data:
749
- timestamp = item.get('createdAt')
750
- if timestamp:
751
- date = datetime.fromisoformat(timestamp[:-1]).date()
752
- activity_dates.append(date)
753
- return activity_dates
754
-
755
- def create_plot(username, year):
756
- dates_in_year = get_dates_in_year(year)
757
- dates_in_year = [d.date() for d in dates_in_year]
758
-
759
- activity_data = get_user_likes(username)
760
- activity_dates = parse_activity_dates(activity_data)
761
-
762
- activity_count = {date: 0 for date in dates_in_year}
763
- for date in activity_dates:
764
- if date in activity_count:
765
- activity_count[date] += 1
766
-
767
- total_activities = sum(activity_count.values())
768
-
769
- # activity->color
770
- def get_color(activity_count):
771
- if activity_count == 0:
772
- return 'white'
773
- elif activity_count < 5:
774
- return 'lightgreen'
775
- elif activity_count < 10:
776
- return 'green'
777
- else:
778
- return 'darkgreen'
779
-
780
- # data->Plotly
781
- num_weeks = len(dates_in_year) // 7 + (1 if len(dates_in_year) % 7 != 0 else 0)
782
-
783
- z = [[None for _ in range(num_weeks)] for _ in range(7)]
784
- hover_texts = [["" for _ in range(num_weeks)] for _ in range(7)]
785
-
786
- for idx, date in enumerate(dates_in_year):
787
- week = idx // 7
788
- day = idx % 7
789
- z[day][week] = activity_count[date]
790
- hover_texts[day][week] = f"{date}: {activity_count[date]} activities"
791
-
792
- # month labels
793
- month_labels = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"]
794
- month_positions = [0] # JAN
795
-
796
- for month in range(2, 13):
797
- first_day_of_month = datetime(year, month, 1).date()
798
- month_position = (first_day_of_month - datetime(year, 1, 1).date()).days // 7
799
- month_positions.append(month_position)
800
-
801
- # heatmap
802
- fig = go.Figure(data=go.Heatmap(
803
- z=z,
804
- x=list(range(num_weeks)),
805
- y=list(range(7)),
806
- hoverinfo='text',
807
- text=hover_texts,
808
- showscale=False,
809
- colorscale=[
810
- [0, 'white'],
811
- [0.25, 'lightgreen'],
812
- [0.5, 'green'],
813
- [1, 'darkgreen']
814
- ],
815
- zmin=0,
816
- zmax=20
817
- ))
818
-
819
- # lock the plot size and ensure cells are square
820
- cell_size = 20
821
- fig.update_layout(
822
- title=f'{username} Activity in {year} | Total Contributions: {total_activities}',
823
- xaxis_nticks=52,
824
- yaxis_nticks=7,
825
- xaxis_title=' ',
826
- yaxis_title=' ',
827
- yaxis=dict(
828
- tickmode='array',
829
- tickvals=list(range(7)),
830
- ticktext=['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
831
- scaleanchor="x", # This ensures that the cells are square
832
- scaleratio=1
833
- ),
834
- xaxis=dict(
835
- tickmode='array',
836
- tickvals=month_positions,
837
- ticktext=month_labels,
838
- scaleanchor="y",
839
- scaleratio=1,
840
- fixedrange=True, # disable zoom and pan
841
- ),
842
- autosize=False,
843
- width=num_weeks * cell_size,
844
- height=14 * cell_size, # halve height of each cell
845
- margin=dict(
846
- l=20,
847
- r=20,
848
- b=20,
849
- t=50,
850
- pad=2
851
- )
852
- )
853
-
854
- fig.update_layout(
855
- xaxis=dict(fixedrange=True), # disable zoom and pan
856
- yaxis=dict(fixedrange=True) # disable zoom and pan
857
- )
858
-
859
- return fig
860
- #-------------------------------------------------------------------------------------------------------------------------------
861
 
 
 
862
  def verify_button(profile: gr.OAuthProfile | None, request: gr.Request) -> str:
863
  url_str = str(request.url)
864
  query_params = parse_qs(urlparse(url_str).query)
@@ -903,21 +177,47 @@ def verify_button(profile: gr.OAuthProfile | None, request: gr.Request) -> str:
903
 
904
  # Remove the token after successful verification
905
  del user_tokens[int(user_id)]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906
 
907
 
908
  demo = gr.Blocks()
909
  with demo:
910
  try:
911
- dataframe1 = pd.read_csv("https://docs.google.com/spreadsheets/d/1C8aLqgCqLYcMiIFf-P_Aosaa03C_WLIB_UyqvjSdWg8/export?format=csv&gid=0") # required for timing
912
- print(dataframe1)
913
-
914
- level_counts = dataframe1['discord_level'].value_counts().sort_index()
915
- dataframe2 = pd.DataFrame({
916
- 'Levels': level_counts.index,
917
- 'Members': level_counts.values
918
- })
919
- print(dataframe2)
920
-
921
  TITLE = """<h1 align="center" id="space-title">πŸ€— Hugging Face Level Leaderboard</h1>"""
922
  gr.HTML(TITLE)
923
  with gr.Tabs(elem_classes="tab-buttons") as tabs:
@@ -942,42 +242,6 @@ with demo:
942
 
943
  login_button.click(check_login_wrapper, inputs=None, outputs=m1)
944
  #------------------------------------------------------------------------------
945
- with gr.TabItem("πŸ… Level leaderboard", elem_id="level-table", id=0):
946
- #gr.Markdown("# πŸ“ˆ Experience Leaderboard")
947
- with gr.Row():
948
- with gr.Column():
949
- gr.DataFrame(get_data, every=5, height=500, datatype=["markdown"], interactive=False, col_count=(3, "fixed"), column_widths=["100px","100px","100px"])
950
-
951
- with gr.Column():
952
- gr.BarPlot(
953
- value=dataframe2,
954
- x="Levels",
955
- y="Members",
956
- title="Level Distribution",
957
- height=450,
958
- width=450,
959
- interactive=False
960
- )
961
- with gr.Row():
962
- gr.Markdown("# πŸ“ˆ How to earn Experience!")
963
- with gr.Row():
964
- gr.DataFrame(get_data2, every=5, interactive=False)
965
- #------------------------------------------------------------------------------
966
- with gr.TabItem("πŸ“ˆ Activity Heatmap", elem_id="activity-heatmap", id=1):
967
- with gr.Row():
968
- username_input = gr.Textbox(label="Enter Username")
969
- with gr.Row():
970
- year_buttons = [gr.Button(str(year)) for year in range(2021, 2025)]
971
- with gr.Row():
972
- plot = gr.Plot(label="Activity Heatmap")
973
-
974
- # buttons for updating the plot
975
- for button in year_buttons:
976
- button.click(
977
- fn=create_plot,
978
- inputs=[username_input, gr.State(int(button.value))],
979
- outputs=plot
980
- )
981
 
982
  #with gr.TabItem("πŸ“ˆ Hub-only leaderboard", elem_id="hub-table", id=2):
983
  except Exception as e:
 
45
  bot = commands.Bot(command_prefix='!', intents=intents)
46
 
47
  GRADIO_APP_URL = "https://huggingface.co/spaces/discord-community/LevelBot"
 
 
48
 
49
 
 
 
 
 
 
 
 
 
 
 
50
  """"""
51
  bot_ids = [1136614989411655780, 1166392942387265536, 1158038249835610123, 1130774761031610388, 1155489509518098565, 1155169841276260546, 1152238037355474964, 1154395078735953930]
52
  """"""
 
 
 
 
 
 
 
 
53
 
54
  hf_token = os.environ.get("HF_TOKEN")
55
 
 
61
  async def callback(self, interaction: discord.Interaction):
62
  # await interaction.user.send(self.message) # this is for DMs, but users may have DMs disabled
63
  user_id = interaction.user.id
64
+
65
+ guild = interaction.guild
66
+ member = guild.get_member(user_id)
67
+ pending_role = guild.get_role(1380908157475360899) # pending verification role, this is to verify that the discord account actually clicked the button at least once
68
+
69
+ if member and pending_role:
70
+ await member.add_roles(pending_role)
71
+ print(f"Assigned 'Pending Verification' role to {member.name}") # should log this in #admin-logs properly
72
+
73
+ unique_link = f"<{GRADIO_APP_URL}?user_id={user_id}>" # don't need token
74
  message = f"Login link generated! To complete the verification process:\n- 1 Visit this link,\n- 2 click the 'πŸ€—Sign in with Hugging Face' button\n\n{unique_link}"
75
  await interaction.response.send_message(message, ephemeral=True)
76
 
77
 
 
 
78
 
79
 
80
  @bot.event
81
  async def on_ready():
82
  try:
83
+
 
84
  print(f'Logged in as {bot.user.name}')
85
+
86
 
87
  guild = bot.get_guild(879548962464493619)
88
  await guild.chunk() # get all users into bot cache
 
103
  print(f"Failed to fetch message with ID 1271145797433557023: {e}")
104
 
105
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
107
 
 
108
 
 
 
109
 
 
 
 
110
  print("------------------------------------------------------------------------")
111
  except Exception as e:
112
+ print(f"on_ready Error: {e}")
 
 
 
 
 
 
 
 
 
113
 
 
114
 
 
 
115
 
 
116
 
 
 
117
 
 
 
 
 
 
 
 
 
 
 
 
118
 
 
 
119
 
 
 
 
 
 
120
 
121
 
122
 
 
 
 
 
 
 
 
123
 
124
 
125
  DISCORD_TOKEN = os.environ.get("DISCORD_TOKEN", None)
 
128
  threading.Thread(target=run_bot).start()
129
 
130
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
 
132
  #-------------------------------------------------------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
 
134
+
135
+ """
136
  def verify_button(profile: gr.OAuthProfile | None, request: gr.Request) -> str:
137
  url_str = str(request.url)
138
  query_params = parse_qs(urlparse(url_str).query)
 
177
 
178
  # Remove the token after successful verification
179
  del user_tokens[int(user_id)]
180
+ """
181
+
182
+
183
+
184
+ def verify_button(profile: gr.OAuthProfile | None, request: gr.Request) -> str:
185
+ query_params = parse_qs(urlparse(str(request.url)).query)
186
+ user_id = query_params.get('user_id', [None])[0]
187
+
188
+ if not user_id:
189
+ return "# ❌ Missing Discord user ID."
190
+
191
+ if profile is None:
192
+ return "# ❌ You're not logged in with Hugging Face."
193
+
194
+ async def upgrade_role():
195
+ guild = bot.get_guild(879548962464493619)
196
+ member = guild.get_member(int(user_id))
197
+ pending_role = guild.get_role(1380908157475360899) # pending
198
+ verified_role = guild.get_role(900063512829755413) # verified
199
+
200
+ if not member:
201
+ print(f"❌ Could not find Discord user {user_id}")
202
+ return
203
+
204
+ if pending_role in member.roles:
205
+ await member.remove_roles(pending_role)
206
+ await member.add_roles(verified_role)
207
+ print(f"βœ… {member.name} verified and upgraded to verified role.")
208
+ else:
209
+ print(f"⚠️ {member.name} did not have the pending role. Ignoring.")
210
+
211
+ asyncio.create_task(upgrade_role())
212
+ return f"# βœ… Verification successful! Welcome, {profile.username} πŸŽ‰"
213
+
214
+
215
+
216
 
217
 
218
  demo = gr.Blocks()
219
  with demo:
220
  try:
 
 
 
 
 
 
 
 
 
 
221
  TITLE = """<h1 align="center" id="space-title">πŸ€— Hugging Face Level Leaderboard</h1>"""
222
  gr.HTML(TITLE)
223
  with gr.Tabs(elem_classes="tab-buttons") as tabs:
 
242
 
243
  login_button.click(check_login_wrapper, inputs=None, outputs=m1)
244
  #------------------------------------------------------------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
245
 
246
  #with gr.TabItem("πŸ“ˆ Hub-only leaderboard", elem_id="hub-table", id=2):
247
  except Exception as e: