simplex-chat 0.6.1 → 0.7.1

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: f6abe984f48fda66308c887485726f5ea72e703750c328effa1a5e90d2b89134
4
+ data.tar.gz: e88620b62cf4258c52af6eac68b94d9f5991164848a0f6aa52e1e079a500f4e1
5
5
  SHA512:
6
- metadata.gz: 516773178824c494adaa70bef60b323774c8775456eeef1de33ca3bc2131073808d85db998ebade717254286374db2f25b8bec2ba4fd903502b229a70ecc2467
7
- data.tar.gz: 9c4848683f657f145538979603d2d5edfa83e32aaf030b6422b5d60ca38613ff8a517abd30f800c508744e255f48d4a172fa4297e2d9ae7d2f55b6ef948db5e1
6
+ metadata.gz: 4fe774622151042e7774640645213ba3f709e8b42719d0344f7c2176ea12c57a803eb7c30fe5051e8c8c56e361cb362ad691247766a5fcc570a9889bc03b8a25
7
+ data.tar.gz: 8ecef57f4c0b4d39a0e8bf75d821f67e08c06850f97fbae1db7a51bd165bf1cd38b75ecf4b5fc5985375d0455c9ca1fd7e36b550229a12cebf5b3b5c6e263ab6
@@ -0,0 +1,170 @@
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
+ begin
24
+ execute client, chat_msg, args
25
+ rescue
26
+ client.api_send_text_message chat_msg[:chat_type], chat_msg[:sender], "@#{chat_msg[:contact]}: Failed to execute command"
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def validate(client, chat_msg, args)
33
+ msg_text = chat_msg[:msg_text]
34
+ chat_type = chat_msg[:chat_type]
35
+ issuer = chat_msg[:contact]
36
+ issuer_role = chat_msg[:contact_role]
37
+ sender = chat_msg[:sender]
38
+
39
+ # Verify that user has permissions to run the command
40
+ role_hierarchy = {
41
+ GroupMemberRole::MEMBER => 0,
42
+ GroupMemberRole::ADMIN => 1,
43
+ GroupMemberRole::OWNER => 2
44
+ }
45
+ perms = role_hierarchy[issuer_role]
46
+ if issuer_role != nil and perms == nil || perms < role_hierarchy[@min_role]
47
+ client.api_send_text_message chat_type, sender, "@#{issuer}: You do not have permission to run this command (required: #{@min_role})"
48
+ return false
49
+ end
50
+
51
+ # Verify arguments
52
+ if args.length != @num_args
53
+ client.api_send_text_message chat_type, sender, "@#{issuer}: Incorrect number of arguments (required: #{@num_args})"
54
+ return false
55
+ end
56
+
57
+ # Verify per sender cooldown
58
+ # NOTE: This should be the last verification, because
59
+ # it will update the last-validated-runs object
60
+ is_on_cooldown = true
61
+ remaining_cooldown = 0.0
62
+ chat = "#{chat_type}#{sender}"
63
+ chat_and_issuer = "#{chat_type}#{sender}[#{issuer}]"
64
+ @last_runs_lock.synchronize {
65
+ sender_last_run = @last_runs[:per_sender][chat]
66
+ issuer_last_run = @last_runs[:per_issuer][chat_and_issuer]
67
+ now = Time.now
68
+ if sender_last_run != nil && @per_sender_cooldown_secs != nil
69
+ time_diff = now - sender_last_run
70
+ if time_diff < @per_sender_cooldown_secs
71
+ remaining_cooldown = @per_sender_cooldown_secs - time_diff
72
+ end
73
+ end
74
+
75
+ if issuer_last_run != nil && @per_issuer_cooldown_secs != nil
76
+ time_diff = now - issuer_last_run
77
+ if time_diff < @per_issuer_cooldown_secs
78
+ cooldown = @per_issuer_cooldown_secs - time_diff
79
+ remaining_cooldown = [cooldown, remaining_cooldown].max
80
+ end
81
+ end
82
+
83
+ break if remaining_cooldown > 0.0
84
+
85
+ @last_runs[:per_sender][chat] = now
86
+ @last_runs[:per_issuer][chat_and_issuer] = now
87
+ is_on_cooldown = false
88
+ }
89
+
90
+ if is_on_cooldown
91
+ client.api_send_text_message chat_type, sender, "@#{issuer}: On cooldown, try again in #{remaining_cooldown.round(1)} seconds"
92
+ return false
93
+ end
94
+
95
+
96
+ return true
97
+ end
98
+
99
+ def execute(client, chat_msg, args)
100
+ raise NoMethodError.new(
101
+ "[!] Default BasicCommand::execute called, nothing will be done\n" \
102
+ " Extend this class to implement custom execution behavior"
103
+ )
104
+ end
105
+ end
106
+
107
+ class BasicCommandRunner
108
+ def initialize(client, commands, prefix)
109
+ @client = client
110
+ @commands = commands.map { |cmd|
111
+ { "#{prefix}#{cmd.name}" => cmd }
112
+ }.reduce({}, &:merge)
113
+ @prefix = prefix
114
+ @logger = Logging.logger
115
+ end
116
+
117
+ def listen(max_backlog_secs: 5.0)
118
+ loop do
119
+ begin
120
+ break if process_next_event(max_backlog_secs) == :stop
121
+ rescue SimpleXChat::GenericError => e
122
+ @logger.error("[!] Caught error: #{e}")
123
+ rescue => e
124
+ raise e
125
+ end
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def process_next_event(max_backlog_secs)
132
+ chat_msg = @client.next_chat_message(max_backlog_secs: max_backlog_secs)
133
+ if chat_msg == nil
134
+ @logger.warn("Message queue is closed")
135
+ return :stop
136
+ end
137
+ @logger.debug("Chat message: #{chat_msg}")
138
+
139
+ msg_text = chat_msg[:msg_text]
140
+ chat_type = chat_msg[:chat_type]
141
+ issuer = chat_msg[:contact]
142
+ issuer_role = chat_msg[:contact_role]
143
+ sender = chat_msg[:sender]
144
+
145
+ # Skip automated group messages
146
+ return if issuer == nil
147
+
148
+ # Verify if message is a command
149
+ message_items = msg_text.split(" ")
150
+ first_word = message_items[0]
151
+ return if not first_word.start_with?(@prefix)
152
+
153
+ # React to all messages we will process
154
+ @client.api_reaction chat_msg[:chat_type], chat_msg[:sender_id], chat_msg[:msg_item_id], emoji: '🚀'
155
+
156
+ # Verify if this is a registered command
157
+ command = @commands[first_word]
158
+ if command == nil
159
+ @client.api_send_text_message chat_msg[:chat_type], chat_msg[:sender], "@#{issuer}: Unknown command"
160
+ return
161
+ end
162
+
163
+ args = message_items[1..]
164
+
165
+ # Run command
166
+ @logger.debug("Validating and executing command '#{command.name}' for: #{chat_type}#{sender} [#{issuer}]: #{msg_text}")
167
+ command.validate_and_execute @client, chat_msg, args
168
+ end
169
+ end
170
+ 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.1"
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.1
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-13 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