From 6532bd250db3eb50638481b72772380afdfc0972 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 4 Apr 2026 17:10:19 +0200 Subject: [PATCH 1/4] feat: add hourly rate limits for agent triggers - Add AGENT_HOURLY env var (default: 60 triggers per hour) - Track hourly usage in SHSF database with key 'agent_{AGENT_USERNAME}_cooldown' - Check and increment counter before sending notifications - Add 'clear_limit' route to reset counter hourly via external trigger Resolves #4 --- __pycache__/main.cpython-313.pyc | Bin 0 -> 16407 bytes main.py | 67 +++++++++++++++++++++++++++++-- 2 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 __pycache__/main.cpython-313.pyc diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d82d9ee22eb57794732f837d187b313c8e901952 GIT binary patch literal 16407 zcmd6Odr%umnrG?#7N7@6JVfy_5A!k)gYm0n8?d{La8syFH+HLKC_pwybV;<0cE(HG z#YW)Sy)f>XZo}@xnz)$QRbSl2Xf8HlxZQ~uyJO;}@4r%uS#&k*EO*y2ck>@@_sq_E z?jr8{vQ#BOi|)C*y@)M>R8{4dnO|mp`8|G_^}=E?QgHqI=2YlkzCuy|4IiXSo4tAQ zse+gIts8NE)L|V?L6`B-2=KSZj|SB{O4@xROg0 zbx@4;bp=<<6fwmeO0I+{;cSepoZ{?^9a0D5fYix2AuVM}AuVIdAayY=NZpJZ(sHI8 z(h8;m(n_Wh(yCcSbJeeK0DR2~K|dXg1#bnTtY99!Ht4%})q6eQAG<18<)`t{*9Lup zU4FhcHahWU!0R8soJTY=dTH>gP$CnJU%ujd?TT+$P|Zi~3eHR3aqk5$GZbDr#A)V$vi<-mGMC@&5V1; z{S58D2AmBV+vqnVOY~iqW9iA@R4fz;2cj|VCLN2=!7x1;TV7; z(zllB$tWA1X1SZ~MlN`lX5WLhX&ll4<2&!Xb2}Od8z-TvPp6Jn#O2*OPiXn}LFI6FU&ISX{~R%9s#x1lJs6b#R@BtJBRwb0SI$kO~YJ&oO52Ac3} zmOd0~H(m+PM7Ra9me^b{MoP>D-(zuDZea>Vv32$yuqSg59Na}}AcA_s>^-Q8jBX^{ z7^ClmBX`Ao(mswBVKWk*f*R-8WtwB7i;?hjD^P`|=4jwMi0@zw$;8kR!a7Gsm!mOu z0Y+pTSQw53=aCc|jYj5?MeHz<2s*;i*T(2zlzw+9G<9bl1{22jZYVZ~@8(0X*gQ+$ z3c?)Vd+4&5u^55Tyfx3ZLWOh;rU0s*Wn**@1~xY@Au>imlo)9)W`Gvx0da{4iU3!HIhI_`A{Y3Gg0*EN0vbSx7c400 z(NHYHEeAr=f;POgaEs-l*aMoT1szIV`{J@t4C4}zstVj;mpKoR)WLuB3M4C3Mpv>n zk=9jhmM89}Yff&pd{UGixSlDg*=*aaPAZbek}kezfY%RX4EDAAX+uM%#Iyd*^+@9N z#CwT3zNnAa_hn2)FQ77N3I|cTG;l)(^cpc!lUYMUQ4==5suy$M3akCGPEjU`hT6kg><9KHsuZ7Rs2XZgHo`h82=#?^ zlNPCUM(Njq7%3%AX{s7bdRXVxVJ+g_jADj5tE#4E6wNB1plFRB8v>=o!b*?8GK5tK z0s;$%=vFpL&v20i`qG5~x>-kNT~L8A2*ro>y`YIM!cv1Ds9{ks4Bne!7je}Hnnh5K zv1YxXoW8}C1E*XCCY6u~Dv+goA%((%A}A=>D69;WL2_|bNb5i$jDoC!l9k!x+8_%C zjEZhRvO?_|%hv~zGil@5m5aYHSu;f?tA>50Msp_PDu1Hg&~8_EB<*Qe@2ce&CRe7h zXQy!>)i|)#z1?^|Z6Eox@%(1ZPy3$r{q)qgudI0=4?i0I@Jg!je9At;n@9Fll-a$n zr_9!sEC2XhM>%|o-|m_mFDQlP%&y+T7oSYNo1EgU13UWDy#92?WLdfLyL}DhzKnJN zcaJ+SD5+;Um-nptS)BvZJ|(_6tAX@mrSU?8>SMF@0p=Nc>mW~# zr-5!3q=!)yS_L5c*_9O5`n8gT2Fp-2jpetSZGTd77paLiq*X^l{Xktn@^jRFjkLy) z`jG6peqY(0EhM>I4e%DP$;mDejXc2xgPAY3>f4b}SWrbl!t0=+g`WH{LI|H2amo7zU_+R33xmq$zUIm!4t`vq@B0+ z?&wdZ^e2C}uTp6GUPf^Nec0};q@HO?F|9Ou grvwBado}JRbePN~hJt+18^?NAY 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 literal 0 HcmV?d00001 diff --git a/main.py b/main.py index 6a587d2..29287ed 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,7 @@ OPENCLAW_THINKING = os.getenv("OPENCLAW_THINKING", "low") DATABASE_STORAGE = os.getenv("DATABASE_STORAGE_NAME") AGENT_USERNAME = os.getenv("AGENT_USERNAME") AGENT_PROMPT_FILE = os.getenv("AGENT_PROMPT_FILE") +AGENT_HOURLY = int(os.getenv("AGENT_HOURLY", "60")) eventsToHandle = ["pull_request", "issues", "issue_comment"] actionsToHandle = ["assigned", "created"] @@ -76,6 +77,43 @@ def delete_stored_assignees(db, event_type, repository_id, number): print(f"Failed to delete assignees from DB: {e}") +def get_rate_limit_key(): + return f"agent_{AGENT_USERNAME}_cooldown" + + +def check_and_increment_rate_limit(db): + """Check if rate limit is reached, increment counter if not. Returns True if allowed, False if limit reached.""" + key = get_rate_limit_key() + try: + current = db.get(DATABASE_STORAGE, key) + if current is None: + current = 0 + else: + current = int(current) + + if current >= AGENT_HOURLY: + print(f"Rate limit reached: {current}/{AGENT_HOURLY} hourly triggers used.") + return False + + db.set(DATABASE_STORAGE, key, str(current + 1)) + print(f"Rate limit check passed: {current + 1}/{AGENT_HOURLY} hourly triggers used.") + return True + except Exception as e: + print(f"Failed to check/increment rate limit: {e}") + # On error, allow the request to proceed + return True + + +def reset_rate_limit(db): + """Reset the hourly rate limit counter.""" + key = get_rate_limit_key() + try: + db.set(DATABASE_STORAGE, key, "0") + print(f"Rate limit reset for agent {AGENT_USERNAME}.") + except Exception as e: + print(f"Failed to reset rate limit: {e}") + + def fill_template(template, event_object, action_str, type_str): fields = { "action_str": action_str, @@ -129,7 +167,7 @@ def build_message(event_object, action_str, type_str): return message -def sendToAgent(event_object): +def sendToAgent(event_object, db): headers = {"x-openclaw-token": OPENCLAW_TOKEN, "Content-Type": "application/json"} print(f"Preparing to send notification to Agent for {json.dumps(event_object)}") @@ -155,6 +193,11 @@ def sendToAgent(event_object): ) return + # Check rate limit before sending + if not check_and_increment_rate_limit(db): + print("Rate limit reached. Skipping notification to agent.") + return + message = build_message(event_object, action_str, type_str) try: @@ -355,7 +398,7 @@ def main(args): # Send to OpenClaw if action == "assigned": - sendToAgent(event_object) + sendToAgent(event_object, db) else: print(f"Action {action} is not configured to send to agent") @@ -381,7 +424,7 @@ def main(args): "url": comment_data.get("html_url"), } - sendToAgent(event_object) + sendToAgent(event_object, db) return { "_shsf": "v2", @@ -389,6 +432,24 @@ def main(args): "_res": {"status": "received"}, "_headers": {"Content-Type": "application/json"}, } + elif route == "clear_limit": + # Reset the hourly rate limit counter + try: + reset_rate_limit(db) + return { + "_shsf": "v2", + "_code": 200, + "_res": {"status": "Rate limit reset"}, + "_headers": {"Content-Type": "application/json"}, + } + except Exception as e: + print(f"Failed to reset rate limit: {e}") + return { + "_shsf": "v2", + "_code": 500, + "_res": {"error": "Failed to reset rate limit"}, + "_headers": {"Content-Type": "application/json"}, + } else: return { "_shsf": "v2", -- 2.39.5 From 6ee9d2429ce8b570eec76005ebaa2cb1c670dcec Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 4 Apr 2026 17:19:16 +0200 Subject: [PATCH 2/4] Add __pycache__/ to .gitignore and remove cached pyc file --- .gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 412c02b..84e3190 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .shsf.json -prompt.txt \ No newline at end of file +prompt.txt +__pycache__/ -- 2.39.5 From 102c146b601f138df5429e13ae1c15ade81eb4ad Mon Sep 17 00:00:00 2001 From: Space Date: Sat, 4 Apr 2026 17:20:01 +0200 Subject: [PATCH 3/4] Delete __pycache__/main.cpython-313.pyc --- __pycache__/main.cpython-313.pyc | Bin 16407 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 __pycache__/main.cpython-313.pyc diff --git a/__pycache__/main.cpython-313.pyc b/__pycache__/main.cpython-313.pyc deleted file mode 100644 index d82d9ee22eb57794732f837d187b313c8e901952..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16407 zcmd6Odr%umnrG?#7N7@6JVfy_5A!k)gYm0n8?d{La8syFH+HLKC_pwybV;<0cE(HG z#YW)Sy)f>XZo}@xnz)$QRbSl2Xf8HlxZQ~uyJO;}@4r%uS#&k*EO*y2ck>@@_sq_E z?jr8{vQ#BOi|)C*y@)M>R8{4dnO|mp`8|G_^}=E?QgHqI=2YlkzCuy|4IiXSo4tAQ zse+gIts8NE)L|V?L6`B-2=KSZj|SB{O4@xROg0 zbx@4;bp=<<6fwmeO0I+{;cSepoZ{?^9a0D5fYix2AuVM}AuVIdAayY=NZpJZ(sHI8 z(h8;m(n_Wh(yCcSbJeeK0DR2~K|dXg1#bnTtY99!Ht4%})q6eQAG<18<)`t{*9Lup zU4FhcHahWU!0R8soJTY=dTH>gP$CnJU%ujd?TT+$P|Zi~3eHR3aqk5$GZbDr#A)V$vi<-mGMC@&5V1; z{S58D2AmBV+vqnVOY~iqW9iA@R4fz;2cj|VCLN2=!7x1;TV7; z(zllB$tWA1X1SZ~MlN`lX5WLhX&ll4<2&!Xb2}Od8z-TvPp6Jn#O2*OPiXn}LFI6FU&ISX{~R%9s#x1lJs6b#R@BtJBRwb0SI$kO~YJ&oO52Ac3} zmOd0~H(m+PM7Ra9me^b{MoP>D-(zuDZea>Vv32$yuqSg59Na}}AcA_s>^-Q8jBX^{ z7^ClmBX`Ao(mswBVKWk*f*R-8WtwB7i;?hjD^P`|=4jwMi0@zw$;8kR!a7Gsm!mOu z0Y+pTSQw53=aCc|jYj5?MeHz<2s*;i*T(2zlzw+9G<9bl1{22jZYVZ~@8(0X*gQ+$ z3c?)Vd+4&5u^55Tyfx3ZLWOh;rU0s*Wn**@1~xY@Au>imlo)9)W`Gvx0da{4iU3!HIhI_`A{Y3Gg0*EN0vbSx7c400 z(NHYHEeAr=f;POgaEs-l*aMoT1szIV`{J@t4C4}zstVj;mpKoR)WLuB3M4C3Mpv>n zk=9jhmM89}Yff&pd{UGixSlDg*=*aaPAZbek}kezfY%RX4EDAAX+uM%#Iyd*^+@9N z#CwT3zNnAa_hn2)FQ77N3I|cTG;l)(^cpc!lUYMUQ4==5suy$M3akCGPEjU`hT6kg><9KHsuZ7Rs2XZgHo`h82=#?^ zlNPCUM(Njq7%3%AX{s7bdRXVxVJ+g_jADj5tE#4E6wNB1plFRB8v>=o!b*?8GK5tK z0s;$%=vFpL&v20i`qG5~x>-kNT~L8A2*ro>y`YIM!cv1Ds9{ks4Bne!7je}Hnnh5K zv1YxXoW8}C1E*XCCY6u~Dv+goA%((%A}A=>D69;WL2_|bNb5i$jDoC!l9k!x+8_%C zjEZhRvO?_|%hv~zGil@5m5aYHSu;f?tA>50Msp_PDu1Hg&~8_EB<*Qe@2ce&CRe7h zXQy!>)i|)#z1?^|Z6Eox@%(1ZPy3$r{q)qgudI0=4?i0I@Jg!je9At;n@9Fll-a$n zr_9!sEC2XhM>%|o-|m_mFDQlP%&y+T7oSYNo1EgU13UWDy#92?WLdfLyL}DhzKnJN zcaJ+SD5+;Um-nptS)BvZJ|(_6tAX@mrSU?8>SMF@0p=Nc>mW~# zr-5!3q=!)yS_L5c*_9O5`n8gT2Fp-2jpetSZGTd77paLiq*X^l{Xktn@^jRFjkLy) z`jG6peqY(0EhM>I4e%DP$;mDejXc2xgPAY3>f4b}SWrbl!t0=+g`WH{LI|H2amo7zU_+R33xmq$zUIm!4t`vq@B0+ z?&wdZ^e2C}uTp6GUPf^Nec0};q@HO?F|9Ou grvwBado}JRbePN~hJt+18^?NAY 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 -- 2.39.5 From 80510370b0a7b92134e56c3b823a20d615aad072 Mon Sep 17 00:00:00 2001 From: Luna Date: Sat, 4 Apr 2026 17:23:00 +0200 Subject: [PATCH 4/4] docs: add AGENT_HOURLY env variable to README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 8da71e7..70187e1 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ The following environment variables are required for the service to function: | `DATABASE_STORAGE_NAME` | The name of the database storage to use. | | `AGENT_USERNAME` | The Gitea username of the AI agent (e.g., `whateveryouragentisnamed`). | | `AGENT_PROMPT_FILE` | (Optional) Path to a custom Markdown template for notifications. If not provided, a default template will be used. | +| `AGENT_HOURLY` | (Optional) Maximum number of triggers allowed per hour. Defaults to `60`. Used for rate limiting. | ## How it works -- 2.39.5