Updated karma 1 hour per target rate limit. Updated requirements.txt and README.md

This commit is contained in:
2026-05-09 23:16:19 -05:00
parent 733e1b43c5
commit a1ce95f72f
3 changed files with 827 additions and 190 deletions
+32 -34
View File
@@ -37,13 +37,14 @@ from datetime import datetime, timedelta
import re
import asyncio
import traceback
import time
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
# Prevent spam: minimum seconds between karma changes to same target by same user
COOLDOWN_SECONDS = 5
# Global cooldown: one karma point per hour per voter
COOLDOWN_SECONDS = 3600
# Database file
DB_FILE = "karma.db"
@@ -55,7 +56,6 @@ display_name_cache = {}
# Last time we refreshed the cache (per room)
cache_timestamp = {}
# ---------------------------------------------------------------------------
# Helper: pluralize "point" vs "points"
# ---------------------------------------------------------------------------
@@ -130,37 +130,31 @@ async def refresh_display_name_cache(bot, room_id):
logging.info(f"Refreshing display name cache for room {room_id}")
try:
# Try to get room members from the bot's state
if hasattr(bot, 'async_client') and bot.async_client:
# Get the room state
room = bot.async_client.rooms.get(room_id)
if room and hasattr(room, 'users'):
# Build mapping of display names to user IDs
resp = await bot.async_client.joined_members(room_id)
if resp.members is not None:
name_map = {}
for user_id, user_info in room.users.items():
# Get display name - try different attributes
display_name = None
if hasattr(user_info, 'display_name') and user_info.display_name:
display_name = user_info.display_name
elif hasattr(user_info, 'name') and user_info.name:
display_name = user_info.name
for member in resp.members:
display_name = (member.display_name or "").strip()
if display_name:
name_map[display_name.lower()] = user_id
# Also store without emojis for easier matching
name_map[display_name.lower()] = member.user_id
# Also store without emojis
clean_name = re.sub(r'[^\w\s]', '', display_name).strip().lower()
if clean_name and clean_name != display_name.lower():
name_map[clean_name] = user_id
name_map[clean_name] = member.user_id
display_name_cache[room_id] = name_map
cache_timestamp[room_id] = now
logging.info(f"Cached {len(name_map)} display names for room {room_id}")
# DEBUG: show first 5 names
sample = list(name_map.items())[:5]
logging.debug(f"Sample display names: {sample}")
return
else:
logging.warning(f"joined_members returned None members for room {room_id}")
except Exception as e:
logging.warning(f"Could not refresh display name cache: {e}")
# If we couldn't get members, initialize empty cache
# init empty cache on failure
display_name_cache[room_id] = {}
cache_timestamp[room_id] = now
@@ -176,7 +170,10 @@ def resolve_display_name(room_id, display_name, bot=None):
Returns None if the input is a Matrix ID (rejected) or if the name
cannot be resolved.
"""
# Reject Matrix IDs outright
# Strip HTML tags (Matrix mention pills)
clean = re.sub(r'<[^>]+>', '', display_name).strip()
# Reject Matrix IDs outright (only if the raw input is an ID, not the cleaned one)
if is_matrix_id(display_name):
return None
@@ -184,21 +181,16 @@ def resolve_display_name(room_id, display_name, bot=None):
if room_id in display_name_cache:
name_map = display_name_cache[room_id]
# Try exact match (case-insensitive)
key = display_name.lower()
# Try exact match (caseinsensitive)
key = clean.lower()
if key in name_map:
return name_map[key]
# Try without emojis/special characters
clean_key = re.sub(r'[^\w\s]', '', display_name).strip().lower()
clean_key = re.sub(r'[^\w\s]', '', clean).strip().lower()
if clean_key and clean_key in name_map:
return name_map[clean_key]
# Try partial match (if display name is contained in a cached name)
for cached_name, user_id in name_map.items():
if key in cached_name or cached_name in key:
return user_id
return None
@@ -567,7 +559,13 @@ async def process_karma_vote(room, display_name, action, voter, bot):
# Check cooldown
if is_on_cooldown(room_id, user_id, voter_str):
remaining = get_cooldown_remaining(room_id, user_id, voter_str)
await bot.api.send_markdown_message(room.room_id, f"⏳ You're doing that too fast! Wait {remaining} seconds.")
hours = remaining // 3600
minutes = (remaining % 3600) // 60
if hours > 0:
time_str = f"{hours}h {minutes}m"
else:
time_str = f"{minutes}m"
await bot.api.send_markdown_message(room.room_id, f"⏳ Slow down! You can give karma to that user again in {time_str}.")
return
# Update karma
@@ -635,7 +633,7 @@ async def handle_karma_command(room, message, bot, config):
<strong>Notes:</strong>
<ul>
<li>You cannot modify your own karma</li>
<li>There is a {COOLDOWN_SECONDS} second cooldown between votes</li>
<li>There is a 1hour cooldown per user you give karma to</li>
<li>Karma is tracked separately per room</li>
<li>Display names with emojis are supported</li>
</ul>
@@ -784,7 +782,7 @@ async def handle_inline_karma(room, message, bot):
if not matches:
return
logging.info(f"Found inline karma matches: {matches}")
logging.debug(f"Found inline karma matches: {matches}")
responses = []
for display_name, operator in matches: