simplex-chat 0.6.1 → 0.7.0

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: 1085de134ea131845256d9109e043629b6f83ab59adf94625c89287272c9966a
4
- data.tar.gz: e2a1ac6403338b1cc07238d4dc6a2cdb15992a5e938b3df04c98d72d7cce3a93
3
+ metadata.gz: 662e4bdcc4df337d7382a5b1c13dc3ac7f9f89db0ea69d5b97c778f20e0d841c
4
+ data.tar.gz: c9fba2eb82c77f0e35ff434864b4a8443edd41662697f4de46f1c10dd412d059
5
5
  SHA512:
6
- metadata.gz: 516773178824c494adaa70bef60b323774c8775456eeef1de33ca3bc2131073808d85db998ebade717254286374db2f25b8bec2ba4fd903502b229a70ecc2467
7
- data.tar.gz: 9c4848683f657f145538979603d2d5edfa83e32aaf030b6422b5d60ca38613ff8a517abd30f800c508744e255f48d4a172fa4297e2d9ae7d2f55b6ef948db5e1
6
+ metadata.gz: c2f3f4895711d3e1e26b1b1cb843aee645fac8b52d4b6f57621f0ac1ad6966363a9130eea9b48af896d2640547792e9577ae25e47a096d48bbf293d6b634c00f
7
+ data.tar.gz: f5a533224f6058317f47fef4c65874320823dd55517282c84ce30d31d438d365f00e968a39bf688c009a58be68b47fe130e1c76bbba2def81cf11cb46b15b739
@@ -0,0 +1,163 @@
1
+ module SimpleXChat
2
+ class BasicCommand
3
+ attr_reader :name, :num_args, :desc, :min_role
4
+
5
+ # TODO: Allow optional arguments
6
+ def initialize(name, desc="", num_args: 0, min_role: GroupMemberRole::MEMBER,
7
+ per_sender_cooldown_secs: nil, per_issuer_cooldown_secs: nil)
8
+ @name = name
9
+ @num_args = num_args
10
+ @desc = desc
11
+ @min_role = min_role
12
+ @per_sender_cooldown_secs = per_sender_cooldown_secs
13
+ @per_issuer_cooldown_secs = per_issuer_cooldown_secs
14
+ @last_runs = {
15
+ :per_sender => {},
16
+ :per_issuer => {}
17
+ }
18
+ @last_runs_lock = Mutex.new
19
+ end
20
+
21
+ def validate_and_execute(client, chat_msg, args)
22
+ return if not validate(client, chat_msg, args)
23
+ execute client, chat_msg, args
24
+ end
25
+
26
+ private
27
+
28
+ def validate(client, chat_msg, args)
29
+ msg_text = chat_msg[:msg_text]
30
+ chat_type = chat_msg[:chat_type]
31
+ issuer = chat_msg[:contact]
32
+ issuer_role = chat_msg[:contact_role]
33
+ sender = chat_msg[:sender]
34
+
35
+ # Verify that user has permissions to run the command
36
+ role_hierarchy = {
37
+ GroupMemberRole::MEMBER => 0,
38
+ GroupMemberRole::ADMIN => 1,
39
+ GroupMemberRole::OWNER => 2
40
+ }
41
+ perms = role_hierarchy[issuer_role]
42
+ if issuer_role != nil and perms == nil || perms < role_hierarchy[@min_role]
43
+ client.api_send_text_message chat_type, sender, "@#{issuer}: You do not have permission to run this command (required: #{@min_role})"
44
+ return false
45
+ end
46
+
47
+ # Verify arguments
48
+ if args.length != @num_args
49
+ client.api_send_text_message chat_type, sender, "@#{issuer}: Incorrect number of arguments (required: #{@num_args})"
50
+ return false
51
+ end
52
+
53
+ # Verify per sender cooldown
54
+ # NOTE: This should be the last verification, because
55
+ # it will update the last-validated-runs object
56
+ is_on_cooldown = true
57
+ remaining_cooldown = 0.0
58
+ chat = "#{chat_type}#{sender}"
59
+ chat_and_issuer = "#{chat_type}#{sender}[#{issuer}]"
60
+ @last_runs_lock.synchronize {
61
+ sender_last_run = @last_runs[:per_sender][chat]
62
+ issuer_last_run = @last_runs[:per_issuer][chat_and_issuer]
63
+ now = Time.now
64
+ if sender_last_run != nil && @per_sender_cooldown_secs != nil
65
+ time_diff = now - sender_last_run
66
+ if time_diff < @per_sender_cooldown_secs
67
+ remaining_cooldown = @per_sender_cooldown_secs - time_diff
68
+ end
69
+ end
70
+
71
+ if issuer_last_run != nil && @per_issuer_cooldown_secs != nil
72
+ time_diff = now - issuer_last_run
73
+ if time_diff < @per_issuer_cooldown_secs
74
+ cooldown = @per_issuer_cooldown_secs - time_diff
75
+ remaining_cooldown = [cooldown, remaining_cooldown].max
76
+ end
77
+ end
78
+
79
+ break if remaining_cooldown > 0.0
80
+
81
+ @last_runs[:per_sender][chat] = now
82
+ @last_runs[:per_issuer][chat_and_issuer] = now
83
+ is_on_cooldown = false
84
+ }
85
+
86
+ if is_on_cooldown
87
+ client.api_send_text_message chat_type, sender, "@#{issuer}: On cooldown, try again in #{remaining_cooldown.round(1)} seconds"
88
+ return false
89
+ end
90
+
91
+
92
+ return true
93
+ end
94
+
95
+ def execute(client, chat_msg, args)
96
+ raise NoMethodError.new(
97
+ "[!] Default BasicCommand::execute called, nothing will be done\n" \
98
+ " Extend this class to implement custom execution behavior"
99
+ )
100
+ end
101
+ end
102
+
103
+ class BasicCommandRunner
104
+ def initialize(client, commands, prefix)
105
+ @client = client
106
+ @commands = commands.map { |cmd|
107
+ { "#{prefix}#{cmd.name}" => cmd }
108
+ }.reduce({}, &:merge)
109
+ @prefix = prefix
110
+ @logger = Logging.logger
111
+ end
112
+
113
+ def listen(max_backlog_secs: 5.0)
114
+ loop do
115
+ begin
116
+ break if process_next_event(max_backlog_secs) == :stop
117
+ rescue SimpleXChat::GenericError => e
118
+ @logger.error("[!] Caught error: #{e}")
119
+ rescue => e
120
+ raise e
121
+ end
122
+ end
123
+ end
124
+
125
+ private
126
+
127
+ def process_next_event(max_backlog_secs)
128
+ chat_msg = @client.next_chat_message(max_backlog_secs: max_backlog_secs)
129
+ if chat_msg == nil
130
+ @logger.warn("Message queue is closed")
131
+ return :stop
132
+ end
133
+ @logger.debug("Chat message: #{chat_msg}")
134
+
135
+ msg_text = chat_msg[:msg_text]
136
+ chat_type = chat_msg[:chat_type]
137
+ issuer = chat_msg[:contact]
138
+ issuer_role = chat_msg[:contact_role]
139
+ sender = chat_msg[:sender]
140
+
141
+ # Verify if this is a registered command
142
+ message_items = msg_text.split(" ")
143
+ first_word = message_items[0]
144
+
145
+ # React to all messages we will process
146
+ if first_word.start_with?(@prefix)
147
+ @client.api_reaction chat_msg[:chat_type], chat_msg[:sender_id], chat_msg[:msg_item_id], emoji: '🚀'
148
+ end
149
+
150
+ command = @commands[first_word]
151
+ if command == nil
152
+ @client.api_send_text_message chat_msg[:chat_type], chat_msg[:sender], "@#{issuer}: Unknown command"
153
+ return
154
+ end
155
+
156
+ args = message_items[1..]
157
+
158
+ # Run command
159
+ @logger.debug("Validating and executing command '#{command.name}' for: #{chat_type}#{sender} [#{issuer}]: #{msg_text}")
160
+ command.validate_and_execute @client, chat_msg, args
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,18 @@
1
+ require 'logger'
2
+
3
+ module SimpleXChat
4
+ module Logging
5
+ def self.logger(dest: $stderr, log_level: Logger::INFO)
6
+ if @logger == nil
7
+ @logger = Logger.new(dest)
8
+ @logger.level = log_level
9
+ @logger.progname = 'simplex-chat'
10
+ @logger.formatter = -> (severity, datetime, progname, msg) {
11
+ "| [#{severity}] | #{datetime} | (#{progname}) :: #{msg}\n"
12
+ }
13
+ end
14
+
15
+ @logger
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SimpleXChat
4
- VERSION = "0.6.1"
4
+ VERSION = "0.7.0"
5
5
  end
data/lib/simplex-chat.rb CHANGED
@@ -4,6 +4,7 @@ require_relative 'simplex-chat/version'
4
4
  require_relative 'simplex-chat/errors'
5
5
  require_relative 'simplex-chat/patches'
6
6
  require_relative 'simplex-chat/types'
7
+ require_relative 'simplex-chat/logger'
7
8
 
8
9
  module SimpleXChat
9
10
  require 'net/http'
@@ -14,9 +15,7 @@ module SimpleXChat
14
15
  require 'time'
15
16
 
16
17
  class ClientAgent
17
- attr_accessor :on_message
18
-
19
- def initialize(client_uri, connect: true, log_level: Logger::INFO, timeout_ms: 10_000, interval_ms: 100)
18
+ def initialize(client_uri, connect: true, timeout_ms: 10_000, interval_ms: 100)
20
19
  @uri = client_uri
21
20
  @message_queue = Queue.new
22
21
  @chat_message_queue = Queue.new
@@ -30,12 +29,7 @@ module SimpleXChat
30
29
  @timeout_ms = timeout_ms
31
30
  @interval_ms = interval_ms
32
31
 
33
- @logger = Logger.new($stderr)
34
- @logger.level = log_level
35
- @logger.progname = 'simplex-chat'
36
- @logger.formatter = -> (severity, datetime, progname, msg) {
37
- "| [#{severity}] | #{datetime} | (#{progname}) :: #{msg}\n"
38
- }
32
+ @logger = Logging.logger
39
33
 
40
34
  self.connect if connect
41
35
 
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simplex-chat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.1
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rdbo
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-05 00:00:00.000000000 Z
10
+ date: 2025-03-12 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: websocket
@@ -48,7 +48,9 @@ files:
48
48
  - README.md
49
49
  - Rakefile
50
50
  - lib/simplex-chat.rb
51
+ - lib/simplex-chat/cmd-runner.rb
51
52
  - lib/simplex-chat/errors.rb
53
+ - lib/simplex-chat/logger.rb
52
54
  - lib/simplex-chat/patches.rb
53
55
  - lib/simplex-chat/types.rb
54
56
  - lib/simplex-chat/version.rb