fluxerrb 0.0.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 +7 -0
- data/README.md +62 -0
- data/lib/fluxerrb/client.rb +208 -0
- data/lib/fluxerrb/version.rb +3 -0
- data/lib/fluxerrb/webhooks.rb +48 -0
- data/lib/fluxerrb.rb +8 -0
- metadata +91 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: a1afcd0591253a04f54e8458bcb1a51733f78ecf3afdb7a47a3a22fca8f61949
|
|
4
|
+
data.tar.gz: 34ca494197af5511d3ef6f73c7358f6529a8554d9285701e1272b8bf9b9dce0a
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 924dca7689bbd5ccca3b6c917c520597d0f8e9c266becb438232d1572d2a71b1374b345361eac0b26c0904e1d6731ca63356d91a2379518fe4c5b232f6d5f047
|
|
7
|
+
data.tar.gz: b56704e5a38fde8f804a0f652f1495c2b6c4b9739e724ed286abf0e908e93e31d8d99f40b4966be4bb9146438ff8b1ebbcd6d42bc116d7ca26ac59b7c33114ac
|
data/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# fluxerrb
|
|
2
|
+
|
|
3
|
+
Fluxerrb is a Ruby package (a.k.a. Gem) that allows you to make Fluxer.app bots and use Fluxer.app webhooks using the Ruby programming language. This package (a.k.a. Gem) is not officially endorsed by fluxer.app and this is not an official fluxer.app product.
|
|
4
|
+
|
|
5
|
+
You need Ruby 3.0 or newer in order to use this package (Ruby 3.2 or newer is recommended)
|
|
6
|
+
|
|
7
|
+
> [!NOTE]
|
|
8
|
+
> This package (a.k.a. Gem) is in an early alpha state so expect things to be buggy and/or broken.
|
|
9
|
+
|
|
10
|
+
## Setup
|
|
11
|
+
|
|
12
|
+
You can install Fluxerrb through the following methods:
|
|
13
|
+
|
|
14
|
+
#### Method 1: Install from Gemfile
|
|
15
|
+
|
|
16
|
+
Add the following to your Gemfile file and run the "[bundle install](https://rubygems.org/gems/fluxerrb)" command:
|
|
17
|
+
|
|
18
|
+
```ruby
|
|
19
|
+
gem 'fluxerrb'
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
or add the following to your Gemfile file to install with Git.
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
gem 'fluxerrb', git: 'https://codeberg.org/roxannewolf/fluxerrb'
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
You can make a simple bot like this:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
require 'fluxerrb'
|
|
34
|
+
|
|
35
|
+
bot = Fluxerrb::Bot.new(token: 'YOUR_BOT_TOKEN_HERE', prefix: '!')
|
|
36
|
+
|
|
37
|
+
bot.on :message do |msg|
|
|
38
|
+
next if msg['author'].nil? || msg['author']['bot'] == true
|
|
39
|
+
puts "New Message: #{msg['content']}"
|
|
40
|
+
|
|
41
|
+
if msg['content'].downcase == 'ping'
|
|
42
|
+
bot.send_message(msg['channel_id'], "Pong!")
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
bot.start
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Webhook example:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
require 'fluxerrb'
|
|
53
|
+
|
|
54
|
+
webhook = Fluxerrb::Webhook.from_url("<WEBHOOK_URL>")
|
|
55
|
+
webhook.send_message("Hello World! This message was sent through a webhook using fluxerrb.")
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
You can view the examples directory for more examples on using this Gem
|
|
59
|
+
|
|
60
|
+
## Support and Help
|
|
61
|
+
|
|
62
|
+
If you need help with this ruby package (a.k.a. Gem), feel free to join the [Roxanne Studios Fluxer Server](https://fluxer.gg/AVbb4B5S) and use the Fluxerrb category to talk about this package.
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
# lib/fluxerrb/client.rb
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'json'
|
|
4
|
+
require 'websocket-client-simple'
|
|
5
|
+
|
|
6
|
+
module Fluxerrb
|
|
7
|
+
class Bot
|
|
8
|
+
attr_accessor :base_url, :token, :prefix, :reconnect_attempts
|
|
9
|
+
|
|
10
|
+
def initialize(config = {})
|
|
11
|
+
@base_url = config[:base_url] || 'https://api.fluxer.app/v1'
|
|
12
|
+
@gateway_url = config[:gateway_url] || 'wss://gateway.fluxer.app/?v=1'
|
|
13
|
+
@token = config[:token]
|
|
14
|
+
@prefix = config[:prefix] || '!'
|
|
15
|
+
|
|
16
|
+
@reconnect_attempts = 0
|
|
17
|
+
@max_reconnects = 5
|
|
18
|
+
@event_handlers = { message: [], command: [] }
|
|
19
|
+
@sequence = nil
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def on(event_type, &block)
|
|
23
|
+
@event_handlers[event_type] << block if @event_handlers.key?(event_type)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def start
|
|
27
|
+
raise "[fluxerrb]: Bot token not set" unless @token
|
|
28
|
+
@running = true
|
|
29
|
+
connect_gateway
|
|
30
|
+
while @running
|
|
31
|
+
sleep 1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def connect_gateway
|
|
38
|
+
puts "[fluxerrb]: Connecting to gateway: #{@gateway_url}..."
|
|
39
|
+
ENV['SSL_CERT_FILE'] = nil
|
|
40
|
+
ENV['OPENSSL_SSL_PROXY_VERIFY'] = 'none'
|
|
41
|
+
OpenSSL::SSL.send(:remove_const, :VERIFY_PEER) if OpenSSL::SSL.const_defined?(:VERIFY_PEER)
|
|
42
|
+
OpenSSL::SSL.const_set(:VERIFY_PEER, OpenSSL::SSL::VERIFY_NONE)
|
|
43
|
+
@ws = WebSocket::Client::Simple.connect(@gateway_url)
|
|
44
|
+
bot_instance = self
|
|
45
|
+
@ws.on :open do
|
|
46
|
+
puts "[fluxerrb]: Connected to Fluxer Gateway!"
|
|
47
|
+
bot_instance.reconnect_attempts = 0
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
@ws.on :message do |msg|
|
|
51
|
+
next if msg.data.nil? || msg.data.empty?
|
|
52
|
+
|
|
53
|
+
begin
|
|
54
|
+
payload = JSON.parse(msg.data)
|
|
55
|
+
bot_instance.send(:handle_gateway_payload, payload)
|
|
56
|
+
rescue JSON::ParserError => e
|
|
57
|
+
puts "JSON Parsing Error: #{e.message} | Raw Data: #{msg.data}"
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@ws.on :close do |e|
|
|
62
|
+
puts "[fluxerrb]: Connection closed by gateway. Code: #{e}"
|
|
63
|
+
bot_instance.send(:stop_heartbeat)
|
|
64
|
+
bot_instance.send(:handle_reconnect)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
@ws.on :error do |e|
|
|
68
|
+
puts "[fluxerrb]: WebSocket Error: #{e}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def handle_gateway_payload(payload)
|
|
73
|
+
@sequence = payload['s'] if payload.key?('s')
|
|
74
|
+
|
|
75
|
+
case payload['op'].to_i
|
|
76
|
+
when 10
|
|
77
|
+
interval = payload['d']['heartbeat_interval'].to_f / 1000.0
|
|
78
|
+
puts "Received HELLO. Heartbeat interval set to #{interval}s."
|
|
79
|
+
start_heartbeat(interval)
|
|
80
|
+
send_identify
|
|
81
|
+
when 11
|
|
82
|
+
# Keep silent as the server acknowledges the ping.
|
|
83
|
+
when 0
|
|
84
|
+
handle_gateway_event(payload)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def start_heartbeat(interval)
|
|
89
|
+
stop_heartbeat
|
|
90
|
+
@heartbeat_thread = Thread.new do
|
|
91
|
+
loop do
|
|
92
|
+
sleep interval
|
|
93
|
+
puts "[fluxerrb]: Sending heartbeat (Sequence: #{@sequence})..."
|
|
94
|
+
send_payload(1, @sequence)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def stop_heartbeat
|
|
100
|
+
@heartbeat_thread&.kill
|
|
101
|
+
@heartbeat_thread = nil
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def send_identify
|
|
105
|
+
identify_data = {
|
|
106
|
+
token: @token,
|
|
107
|
+
intents: 0,
|
|
108
|
+
properties: {os: RUBY_PLATFORM, browser: "fluxerrb", device: "fluxerrb"}
|
|
109
|
+
}
|
|
110
|
+
send_payload(2, identify_data)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def send_payload(op, data)
|
|
114
|
+
return unless @ws && @ws.open?
|
|
115
|
+
@ws.send({ op: op, d: data }.to_json)
|
|
116
|
+
rescue => e
|
|
117
|
+
puts "Failed to transmit payload: #{e.message}"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def handle_reconnect
|
|
121
|
+
return unless @running
|
|
122
|
+
if @reconnect_attempts >= @max_reconnects
|
|
123
|
+
puts "[fluxerrb]: Reconnection attempts failed. Exiting"
|
|
124
|
+
@running = false
|
|
125
|
+
return
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
@reconnect_attempts += 1
|
|
129
|
+
wait_time = @reconnect_attempts * 3
|
|
130
|
+
puts "[fluxerrb]: Attempting to reconnect in #{wait_time} seconds..."
|
|
131
|
+
|
|
132
|
+
sleep wait_time
|
|
133
|
+
connect_gateway if @running
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def handle_gateway_event(payload)
|
|
137
|
+
case payload['t']
|
|
138
|
+
when 'READY'
|
|
139
|
+
puts "Fluxer Bot is ONLINE and READY!"
|
|
140
|
+
when 'MESSAGE_CREATE'
|
|
141
|
+
message_data = payload['d']
|
|
142
|
+
@event_handlers[:message].each { |handler| handler.call(message_data) }
|
|
143
|
+
|
|
144
|
+
if message_data['content']&.start_with?(@prefix)
|
|
145
|
+
raw_content = message_data['content']
|
|
146
|
+
parts = raw_content.slice(@prefix.length..-1).split(' ')
|
|
147
|
+
command_name = parts.first
|
|
148
|
+
args = parts[1..-1] || []
|
|
149
|
+
|
|
150
|
+
ctx = { message: message_data, command: command_name, args: args, content: args.join(' '), channel_id: message_data['channel_id'], client: self }
|
|
151
|
+
|
|
152
|
+
@event_handlers[:command].each { |handler| handler.call(ctx) }
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
public
|
|
158
|
+
|
|
159
|
+
def request(method, path, body = nil)
|
|
160
|
+
uri = URI("#{@base_url}#{path}")
|
|
161
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
162
|
+
if uri.scheme == 'https'
|
|
163
|
+
http.use_ssl = true
|
|
164
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
req_class = Object.const_get("Net::HTTP::#{method.capitalize}")
|
|
168
|
+
req = req_class.new(uri.path, {
|
|
169
|
+
'Authorization' => "Bot #{@token}",
|
|
170
|
+
'Content-Type' => 'application/json'
|
|
171
|
+
})
|
|
172
|
+
req.body = body.to_json if body
|
|
173
|
+
response = http.request(req)
|
|
174
|
+
|
|
175
|
+
if response.code.to_i == 429
|
|
176
|
+
puts "[fluxerrb]: AN ERROR HAS OCCURED: 429 Ratelimit."
|
|
177
|
+
@running = false
|
|
178
|
+
exit(1)
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
JSON.parse(response.body) rescue response.body
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def send_message(channel_id, content = "", embed = nil)
|
|
185
|
+
payload = {}
|
|
186
|
+
payload[:content] = content unless content.empty?
|
|
187
|
+
payload[:embed] = embed if embed
|
|
188
|
+
request('post', "/channels/#{channel_id}/messages", payload)
|
|
189
|
+
end
|
|
190
|
+
def get_channel(channel_id)
|
|
191
|
+
request('get', "/channels/#{channel_id}")
|
|
192
|
+
end
|
|
193
|
+
def get_guild(guild_id)
|
|
194
|
+
request('get', "/guilds/#{guild_id}")
|
|
195
|
+
end
|
|
196
|
+
def nsfw?(channel_or_id)
|
|
197
|
+
channel_data = channel_or_id.is_a?(Hash) ? channel_or_id : get_channel(channel_or_id)
|
|
198
|
+
!!channel_data['nsfw']
|
|
199
|
+
end
|
|
200
|
+
def server_owner?(author_id, guild_or_id)
|
|
201
|
+
guild_data = guild_or_id.is_a?(Hash) ? guild_or_id : get_guild(guild_or_id)
|
|
202
|
+
author_id.to_s == guild_data['owner_id'].to_s
|
|
203
|
+
end
|
|
204
|
+
def user_id?(author_id, target_id)
|
|
205
|
+
author_id.to_s == target_id.to_s
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# lib/fluxerrb/webhooks.rb
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Fluxerrb
|
|
6
|
+
class Webhook
|
|
7
|
+
attr_accessor :base_url, :webhook_id, :webhook_token
|
|
8
|
+
def initialize(config = {})
|
|
9
|
+
@base_url = config[:base_url] || 'https://api.fluxer.app/v1'
|
|
10
|
+
@webhook_id = config[:id]
|
|
11
|
+
@webhook_token = config[:token]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.from_url(url, base_override = nil)
|
|
15
|
+
uri = URI(url)
|
|
16
|
+
parts = uri.path.split('/')
|
|
17
|
+
|
|
18
|
+
token = parts.pop
|
|
19
|
+
id = parts.pop
|
|
20
|
+
|
|
21
|
+
base = base_override || "#{uri.scheme}://#{uri.host}/v1"
|
|
22
|
+
new(base_url: base, id: id, token: token)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def send_message(content, extra_params = {})
|
|
26
|
+
raise "[fluxerrb]: Webhook ID/Token not set" unless @webhook_id && @webhook_token
|
|
27
|
+
|
|
28
|
+
path = "/webhooks/#{@webhook_id}/#{@webhook_token}"
|
|
29
|
+
uri = URI("#{@base_url}#{path}")
|
|
30
|
+
payload = { content: content }.merge(extra_params)
|
|
31
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
32
|
+
if uri.scheme == 'https'
|
|
33
|
+
http.use_ssl = true
|
|
34
|
+
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
35
|
+
end
|
|
36
|
+
req = Net::HTTP::Post.new(uri.path, { 'Content-Type' => 'application/json' })
|
|
37
|
+
req.body = payload.to_json
|
|
38
|
+
response = http.request(req)
|
|
39
|
+
|
|
40
|
+
if response.code.to_i == 429
|
|
41
|
+
puts "[fluxerrb]: AN ERROR HAS OCCURED: 429 Ratelimit."
|
|
42
|
+
return false
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
response.code.to_i == 200 || response.code.to_i == 204
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
data/lib/fluxerrb.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: fluxerrb
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Roxanne Studios
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-01 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: json
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 2.13.2
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 2.13.2
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: net-http
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: 0.6.0
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: 0.6.0
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: websocket-client-simple
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '0.3'
|
|
47
|
+
type: :runtime
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0.3'
|
|
54
|
+
description: The first Ruby package (a.k.a. gem) to exist for making Fluxer.app bots
|
|
55
|
+
and using Fluxer.app webhooks. This project is not officially endorsed by Fluxer.app
|
|
56
|
+
email:
|
|
57
|
+
- ''
|
|
58
|
+
executables: []
|
|
59
|
+
extensions: []
|
|
60
|
+
extra_rdoc_files: []
|
|
61
|
+
files:
|
|
62
|
+
- README.md
|
|
63
|
+
- lib/fluxerrb.rb
|
|
64
|
+
- lib/fluxerrb/client.rb
|
|
65
|
+
- lib/fluxerrb/version.rb
|
|
66
|
+
- lib/fluxerrb/webhooks.rb
|
|
67
|
+
homepage: https://codeberg.org/roxannewolf/fluxerrb
|
|
68
|
+
licenses:
|
|
69
|
+
- MIT
|
|
70
|
+
metadata:
|
|
71
|
+
changelog_uri: https://codeberg.org/roxannewolf/fluxerrb/src/branch/main/CHANGELOG.md
|
|
72
|
+
documentation_uri: https://codeberg.org/roxannewolf/fluxerrb/src/branch/main/README.md
|
|
73
|
+
source_code_uri: https://codeberg.org/roxannewolf/fluxerrb
|
|
74
|
+
rdoc_options: []
|
|
75
|
+
require_paths:
|
|
76
|
+
- lib
|
|
77
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '3.0'
|
|
82
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: '0'
|
|
87
|
+
requirements: []
|
|
88
|
+
rubygems_version: 3.7.2
|
|
89
|
+
specification_version: 4
|
|
90
|
+
summary: Ruby gem for making Fluxer.app bots
|
|
91
|
+
test_files: []
|