brainiac-discord 0.0.20 → 0.0.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0c4e465a488b1eb8ff21dc46d539271cd85a35086fbed35a32e49bad609ee949
|
|
4
|
+
data.tar.gz: 5095805d24f0422c6ad9fe0ea7bb363965732b6d2b6df047d00295ce14b05194
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8c98fba862167e6c12096161dfb5fd964b12c6cb938c767279b00c2002a0770993c0f3a68b6b9f35fd391fbecba83d19f6fb21b1d1972a05aad4adcd89c78ff
|
|
7
|
+
data.tar.gz: 37d9072132b6c961f15c7237f9b5bae98db6ddf0933f39ea9bfee4c9b983a85eb8b007e6cf2b11d8fc19fe75c261f733429cbad1827d124c4c47cbdaa811147e
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require "websocket-client-simple"
|
|
4
4
|
require "websocket"
|
|
5
|
+
require "time"
|
|
5
6
|
|
|
6
7
|
module Brainiac
|
|
7
8
|
module Plugins
|
|
@@ -23,6 +24,12 @@ module Brainiac
|
|
|
23
24
|
BACKOFF_HANDSHAKE_BASE = 30
|
|
24
25
|
BACKOFF_CAP = 300
|
|
25
26
|
|
|
27
|
+
# How long after a message is posted an edit still counts as a real,
|
|
28
|
+
# conversational edit worth re-dispatching on. Edits that land outside
|
|
29
|
+
# this window are treated as server-side embed refreshes (Discord
|
|
30
|
+
# restamping edited_timestamp when it unfurls a link), not human intent.
|
|
31
|
+
EDIT_FRESHNESS_WINDOW = 300 # seconds (5 minutes)
|
|
32
|
+
|
|
26
33
|
# Per-bot state: { agent_key => { token:, user_id:, status:, thread: } }
|
|
27
34
|
@bots = {}
|
|
28
35
|
@bots_mutex = Mutex.new
|
|
@@ -376,7 +383,16 @@ module Brainiac
|
|
|
376
383
|
LOG.error "[Discord:#{agent_display}] Error handling message: #{e.message}\n#{e.backtrace.first(3).join("\n")}" if defined?(LOG)
|
|
377
384
|
end
|
|
378
385
|
when "MESSAGE_UPDATE"
|
|
379
|
-
|
|
386
|
+
# Discord emits MESSAGE_UPDATE for genuine human edits AND for
|
|
387
|
+
# server-side embed (un)furls on existing messages — e.g. lazily
|
|
388
|
+
# generating a link preview days after the message was posted. Both
|
|
389
|
+
# stamp edited_timestamp, so re-dispatching on every update means a
|
|
390
|
+
# bare URL in an old agent-mentioning message can silently wake the
|
|
391
|
+
# agent long after the fact. Only treat an update as actionable when
|
|
392
|
+
# the edit happened close to the message's own creation time, which
|
|
393
|
+
# is what a real conversational edit (typo fix, afterthought) looks
|
|
394
|
+
# like. See message.rb for the shared dispatch path.
|
|
395
|
+
if data["edited_timestamp"] && !stale_edit?(data["timestamp"], data["edited_timestamp"])
|
|
380
396
|
Thread.new do
|
|
381
397
|
Message.handle(data, agent_key, bot_token, bot_user_id)
|
|
382
398
|
rescue StandardError => e
|
|
@@ -384,6 +400,9 @@ module Brainiac
|
|
|
384
400
|
LOG.error "[Discord:#{agent_display}] Error handling message update: #{e.message}\n#{e.backtrace.first(3).join("\n")}"
|
|
385
401
|
end
|
|
386
402
|
end
|
|
403
|
+
elsif data["edited_timestamp"] && defined?(LOG)
|
|
404
|
+
LOG.info "[Discord:#{agent_display}] Ignoring stale MESSAGE_UPDATE " \
|
|
405
|
+
"(likely embed refresh) on message #{data["id"]}"
|
|
387
406
|
end
|
|
388
407
|
when "MESSAGE_REACTION_ADD"
|
|
389
408
|
Thread.new do
|
|
@@ -416,6 +435,25 @@ module Brainiac
|
|
|
416
435
|
bot_user_id
|
|
417
436
|
end
|
|
418
437
|
|
|
438
|
+
# True when a MESSAGE_UPDATE's edit happened long enough after the
|
|
439
|
+
# message was created that it's almost certainly a server-side embed
|
|
440
|
+
# refresh rather than a human edit. Guards against spurious agent
|
|
441
|
+
# dispatches from Discord lazily unfurling link previews on old
|
|
442
|
+
# messages that mention the bot.
|
|
443
|
+
#
|
|
444
|
+
# Fails open (returns false → treat as a real edit) when either
|
|
445
|
+
# timestamp is missing or unparseable, so a parsing hiccup never
|
|
446
|
+
# silently swallows a genuine edit.
|
|
447
|
+
def stale_edit?(created_at, edited_at)
|
|
448
|
+
return false if created_at.nil? || edited_at.nil?
|
|
449
|
+
|
|
450
|
+
created = Time.iso8601(created_at.to_s)
|
|
451
|
+
edited = Time.iso8601(edited_at.to_s)
|
|
452
|
+
(edited - created) > EDIT_FRESHNESS_WINDOW
|
|
453
|
+
rescue ArgumentError
|
|
454
|
+
false
|
|
455
|
+
end
|
|
456
|
+
|
|
419
457
|
def mark_bot_ready(agent_key, agent_display, bot_user_id, data)
|
|
420
458
|
@bots_mutex.synchronize do
|
|
421
459
|
@bots[agent_key][:user_id] = bot_user_id
|
|
@@ -714,8 +714,15 @@ module Brainiac
|
|
|
714
714
|
end
|
|
715
715
|
|
|
716
716
|
def resolve_overrides(clean_content, project_config, thread_model, thread_effort, thread_cli_provider)
|
|
717
|
-
|
|
717
|
+
inline_cli = detect_cli_provider(text: clean_content)
|
|
718
|
+
cli_provider = inline_cli || thread_cli_provider
|
|
718
719
|
has_explicit_model = explicit_model_tag?(clean_content, project_config, cli_provider_override: cli_provider)
|
|
720
|
+
|
|
721
|
+
# The stored model is a resolved ID from the previous provider's model map, so it
|
|
722
|
+
# means nothing to a different provider. Drop it on a switch and fall back to the
|
|
723
|
+
# new provider's default (unless this message also carries a model tag).
|
|
724
|
+
# Effort is kept: it's generic and gets clamped per provider anyway.
|
|
725
|
+
thread_model = nil if provider_switched?(inline_cli, thread_cli_provider)
|
|
719
726
|
has_explicit_effort = clean_content.match?(/\[effort:\w+\]/i)
|
|
720
727
|
|
|
721
728
|
model = if has_explicit_model
|
|
@@ -737,6 +744,10 @@ module Brainiac
|
|
|
737
744
|
[model, effort, cli_provider]
|
|
738
745
|
end
|
|
739
746
|
|
|
747
|
+
def provider_switched?(inline_cli, prev_cli_provider)
|
|
748
|
+
!inline_cli.nil? && inline_cli != prev_cli_provider
|
|
749
|
+
end
|
|
750
|
+
|
|
740
751
|
# Persist inline overrides to the thread map (and work item map if a work item exists).
|
|
741
752
|
# Only writes when a new inline tag was detected (differs from previously stored values).
|
|
742
753
|
def persist_overrides(thread_map_key, clean_content, project_config,
|
|
@@ -761,7 +772,8 @@ module Brainiac
|
|
|
761
772
|
entry = map[thread_map_key]
|
|
762
773
|
if entry
|
|
763
774
|
entry["cli_provider"] = cli_provider if inline_cli
|
|
764
|
-
|
|
775
|
+
# Clear the old provider's model on a switch so it can't resurface later
|
|
776
|
+
entry["model"] = inline_model if inline_model || provider_switched?(inline_cli, prev_cli_provider)
|
|
765
777
|
entry["effort"] = effort if inline_effort
|
|
766
778
|
Config.save_thread_map(map)
|
|
767
779
|
end
|