qwtf_discord_bot 5.0.4 → 5.1.4
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 +4 -4
- data/Gemfile.lock +34 -32
- data/README.md +2 -2
- data/VERSION +1 -1
- data/lib/event_decorator.rb +54 -0
- data/lib/pug.rb +91 -0
- data/lib/qwtf_discord_bot/qwtf_discord_bot_pug.rb +128 -96
- metadata +4 -3
- data/lib/event_wrapper.rb +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2764268ae2786e3217f973d14b910bbed79dd4b1d4b822ebaee31b4f555c9945
|
4
|
+
data.tar.gz: ec2f3fd80b0eaaa222ca9cac071763ef3220190991f94e37a112025002b76cec
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7d86a1207ec73b9225c0a72d530000a6e7ce3297e490bbb914dd45f181be7b1a214c67242b35274545c1f92c4d11911c42e5b6434fc5ecc1dddae33303bfff72
|
7
|
+
data.tar.gz: b654a7a3c136c4847dd9aefc0f4e7e695b7f7dc4a8a8ad521535096d10385c17397f2324bb0a65393da99207934f719523b00a9a1754dd76a368d5bd0001ce75
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
qwtf_discord_bot (
|
4
|
+
qwtf_discord_bot (5.0.4)
|
5
5
|
discordrb (~> 3.3)
|
6
6
|
redis (~> 4.1)
|
7
7
|
thor (~> 0.20)
|
@@ -9,14 +9,15 @@ PATH
|
|
9
9
|
GEM
|
10
10
|
remote: https://rubygems.org/
|
11
11
|
specs:
|
12
|
-
activesupport (
|
12
|
+
activesupport (6.0.3.2)
|
13
13
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
14
14
|
i18n (>= 0.7, < 2)
|
15
15
|
minitest (~> 5.1)
|
16
16
|
tzinfo (~> 1.1)
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
zeitwerk (~> 2.2, >= 2.2.2)
|
18
|
+
coderay (1.1.3)
|
19
|
+
concurrent-ruby (1.1.7)
|
20
|
+
diff-lcs (1.4.4)
|
20
21
|
discordrb (3.3.0)
|
21
22
|
discordrb-webhooks (~> 3.3.0)
|
22
23
|
ffi (>= 1.9.24)
|
@@ -26,60 +27,61 @@ GEM
|
|
26
27
|
websocket-client-simple (>= 0.3.0)
|
27
28
|
discordrb-webhooks (3.3.0)
|
28
29
|
rest-client (>= 2.1.0.rc1)
|
29
|
-
domain_name (0.5.
|
30
|
+
domain_name (0.5.20190701)
|
30
31
|
unf (>= 0.0.5, < 1.0.0)
|
31
32
|
event_emitter (0.2.6)
|
32
|
-
factory_bot (
|
33
|
-
activesupport (>=
|
34
|
-
ffi (1.
|
33
|
+
factory_bot (6.1.0)
|
34
|
+
activesupport (>= 5.0.0)
|
35
|
+
ffi (1.13.1)
|
35
36
|
http-accept (1.7.0)
|
36
37
|
http-cookie (1.0.3)
|
37
38
|
domain_name (~> 0.5)
|
38
|
-
i18n (1.
|
39
|
+
i18n (1.8.5)
|
39
40
|
concurrent-ruby (~> 1.0)
|
40
|
-
method_source (0.
|
41
|
-
mime-types (3.
|
41
|
+
method_source (1.0.0)
|
42
|
+
mime-types (3.3.1)
|
42
43
|
mime-types-data (~> 3.2015)
|
43
|
-
mime-types-data (3.
|
44
|
-
minitest (5.
|
44
|
+
mime-types-data (3.2020.0512)
|
45
|
+
minitest (5.14.1)
|
45
46
|
netrc (0.11.0)
|
46
47
|
opus-ruby (1.0.1)
|
47
48
|
ffi
|
48
|
-
pry (0.
|
49
|
-
coderay (~> 1.1
|
50
|
-
method_source (~>
|
49
|
+
pry (0.13.1)
|
50
|
+
coderay (~> 1.1)
|
51
|
+
method_source (~> 1.0)
|
51
52
|
rbnacl (3.4.0)
|
52
53
|
ffi
|
53
|
-
redis (4.1
|
54
|
-
rest-client (2.1.0
|
54
|
+
redis (4.2.1)
|
55
|
+
rest-client (2.1.0)
|
55
56
|
http-accept (>= 1.7.0, < 2.0)
|
56
57
|
http-cookie (>= 1.0.2, < 2.0)
|
57
58
|
mime-types (>= 1.16, < 4.0)
|
58
59
|
netrc (~> 0.8)
|
59
|
-
rspec (3.
|
60
|
-
rspec-core (~> 3.
|
61
|
-
rspec-expectations (~> 3.
|
62
|
-
rspec-mocks (~> 3.
|
63
|
-
rspec-core (3.
|
64
|
-
rspec-support (~> 3.
|
65
|
-
rspec-expectations (3.
|
60
|
+
rspec (3.9.0)
|
61
|
+
rspec-core (~> 3.9.0)
|
62
|
+
rspec-expectations (~> 3.9.0)
|
63
|
+
rspec-mocks (~> 3.9.0)
|
64
|
+
rspec-core (3.9.2)
|
65
|
+
rspec-support (~> 3.9.3)
|
66
|
+
rspec-expectations (3.9.2)
|
66
67
|
diff-lcs (>= 1.2.0, < 2.0)
|
67
|
-
rspec-support (~> 3.
|
68
|
-
rspec-mocks (3.
|
68
|
+
rspec-support (~> 3.9.0)
|
69
|
+
rspec-mocks (3.9.1)
|
69
70
|
diff-lcs (>= 1.2.0, < 2.0)
|
70
|
-
rspec-support (~> 3.
|
71
|
-
rspec-support (3.
|
71
|
+
rspec-support (~> 3.9.0)
|
72
|
+
rspec-support (3.9.3)
|
72
73
|
thor (0.20.3)
|
73
74
|
thread_safe (0.3.6)
|
74
|
-
tzinfo (1.2.
|
75
|
+
tzinfo (1.2.7)
|
75
76
|
thread_safe (~> 0.1)
|
76
77
|
unf (0.1.4)
|
77
78
|
unf_ext
|
78
|
-
unf_ext (0.0.7.
|
79
|
+
unf_ext (0.0.7.7)
|
79
80
|
websocket (1.2.8)
|
80
81
|
websocket-client-simple (0.3.0)
|
81
82
|
event_emitter
|
82
83
|
websocket
|
84
|
+
zeitwerk (2.4.0)
|
83
85
|
|
84
86
|
PLATFORMS
|
85
87
|
ruby
|
data/README.md
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
5.
|
1
|
+
5.1.4
|
@@ -0,0 +1,54 @@
|
|
1
|
+
class EventDecorator
|
2
|
+
def initialize(event)
|
3
|
+
@event = event
|
4
|
+
end
|
5
|
+
|
6
|
+
def channel
|
7
|
+
@event.channel
|
8
|
+
end
|
9
|
+
|
10
|
+
def channel_id
|
11
|
+
@event.channel.id
|
12
|
+
end
|
13
|
+
|
14
|
+
def display_name
|
15
|
+
user.display_name
|
16
|
+
end
|
17
|
+
|
18
|
+
def user_id
|
19
|
+
user.id
|
20
|
+
end
|
21
|
+
|
22
|
+
def users
|
23
|
+
server.users
|
24
|
+
end
|
25
|
+
|
26
|
+
def mentions_for(user_ids)
|
27
|
+
find_users(user_ids).map(&:mention)
|
28
|
+
end
|
29
|
+
|
30
|
+
def display_names_for(user_ids)
|
31
|
+
find_users(user_ids).map(&:display_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def find_users(user_ids)
|
37
|
+
user_ids.map do |user_id|
|
38
|
+
find_user(user_id)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def find_user(user_id)
|
43
|
+
users.find { |user| user.id == user_id }
|
44
|
+
end
|
45
|
+
|
46
|
+
def server
|
47
|
+
@event.server
|
48
|
+
end
|
49
|
+
|
50
|
+
def user
|
51
|
+
@event.user
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
data/lib/pug.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
class Pug
|
2
|
+
DEFAULT_MAXPLAYERS = 8
|
3
|
+
|
4
|
+
def self.for(channel_id)
|
5
|
+
new(channel_id)
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(channel_id)
|
9
|
+
@channel_id = channel_id
|
10
|
+
end
|
11
|
+
|
12
|
+
def join(user_id)
|
13
|
+
redis.setnx(pug_key, Time.now)
|
14
|
+
redis.sadd(players_key, user_id)
|
15
|
+
end
|
16
|
+
|
17
|
+
def joined_players
|
18
|
+
redis.smembers(players_key).map(&:to_i)
|
19
|
+
end
|
20
|
+
|
21
|
+
def full?
|
22
|
+
joined_player_count >= maxplayers
|
23
|
+
end
|
24
|
+
|
25
|
+
def joined_player_count
|
26
|
+
redis.scard(players_key).to_i
|
27
|
+
end
|
28
|
+
|
29
|
+
def player_slots
|
30
|
+
"#{joined_player_count}/#{maxplayers}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def slots_left
|
34
|
+
maxplayers - joined_player_count
|
35
|
+
end
|
36
|
+
|
37
|
+
def notify_roles=(roles)
|
38
|
+
redis.set(notify_roles_key, roles)
|
39
|
+
end
|
40
|
+
|
41
|
+
def notify_roles
|
42
|
+
redis.get(notify_roles_key) || "@here"
|
43
|
+
end
|
44
|
+
|
45
|
+
def maxplayers=(maxplayers)
|
46
|
+
redis.set(maxplayers_key, maxplayers)
|
47
|
+
end
|
48
|
+
|
49
|
+
def maxplayers
|
50
|
+
(redis.get(maxplayers_key) || DEFAULT_MAXPLAYERS).to_i
|
51
|
+
end
|
52
|
+
|
53
|
+
def active?
|
54
|
+
redis.get(pug_key)
|
55
|
+
end
|
56
|
+
|
57
|
+
def leave(player_id)
|
58
|
+
redis.srem(players_key, player_id)
|
59
|
+
end
|
60
|
+
|
61
|
+
def empty?
|
62
|
+
joined_player_count == 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def end_pug
|
66
|
+
redis.del(pug_key)
|
67
|
+
redis.del(players_key)
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def maxplayers_key
|
73
|
+
[pug_key, "maxplayers"].join(":")
|
74
|
+
end
|
75
|
+
|
76
|
+
def players_key
|
77
|
+
[pug_key, "players"].join(":")
|
78
|
+
end
|
79
|
+
|
80
|
+
def pug_key
|
81
|
+
["pug", "channel", @channel_id].join(":")
|
82
|
+
end
|
83
|
+
|
84
|
+
def notify_roles_key
|
85
|
+
[pug_key, "role"].join(":")
|
86
|
+
end
|
87
|
+
|
88
|
+
def redis
|
89
|
+
Redis.current
|
90
|
+
end
|
91
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require 'pug'
|
4
|
+
require 'event_decorator'
|
5
|
+
|
6
|
+
class QwtfDiscordBotPug # :nodoc:
|
4
7
|
include QwtfDiscordBot
|
5
8
|
|
6
9
|
FOUR_HOURS = 4 * 60 * 60
|
@@ -14,113 +17,132 @@ class QwtfDiscordBotPug
|
|
14
17
|
)
|
15
18
|
|
16
19
|
bot.command :join do |event, *args|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
20
|
+
set_pug(event) do |e, pug|
|
21
|
+
if pug.joined_players.include?(e.user_id)
|
22
|
+
message = "You've already joined"
|
23
|
+
send_and_log_message(message, e.channel)
|
24
|
+
else
|
25
|
+
pug.join(e.user_id)
|
26
|
+
|
27
|
+
message = if pug.joined_player_count == 1
|
28
|
+
[
|
29
|
+
"#{e.display_name} creates a PUG",
|
30
|
+
pug.player_slots,
|
31
|
+
pug.notify_roles
|
32
|
+
].join(' | ')
|
33
|
+
elsif pug.slots_left.between?(1,3)
|
34
|
+
[
|
35
|
+
"#{e.display_name} joins the PUG",
|
36
|
+
pug.player_slots,
|
37
|
+
"#{pug.slots_left} more",
|
38
|
+
pug.notify_roles
|
39
|
+
].join(' | ')
|
40
|
+
else
|
41
|
+
[
|
42
|
+
"#{e.display_name} joins the PUG",
|
43
|
+
pug.player_slots
|
44
|
+
].join(' | ')
|
45
|
+
end
|
46
|
+
|
47
|
+
send_and_log_message(message, e.channel)
|
48
|
+
|
49
|
+
if pug.full?
|
50
|
+
message = start_pug(
|
51
|
+
pug.player_slots,
|
52
|
+
e.mentions_for(pug.joined_players)
|
53
|
+
)
|
54
|
+
|
55
|
+
send_and_log_message(message, e.channel)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
48
59
|
end
|
49
60
|
|
50
61
|
bot.command :status do |event, *args|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
send_and_log_message(message, event)
|
62
|
+
set_pug(event) do |e, pug|
|
63
|
+
message = if pug.active?
|
64
|
+
[
|
65
|
+
"#{e.display_names_for(pug.joined_players).join(', ')} joined",
|
66
|
+
pug.player_slots
|
67
|
+
].join(' | ')
|
68
|
+
else
|
69
|
+
'No PUG has been started. `!join` to create'
|
70
|
+
end
|
71
|
+
|
72
|
+
send_and_log_message(message, e.channel)
|
73
|
+
end
|
64
74
|
end
|
65
75
|
|
66
76
|
bot.command :maxplayers do |event, *args|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
77
|
+
set_pug(event) do |e, pug|
|
78
|
+
new_maxplayers = args[0]
|
79
|
+
|
80
|
+
message = if new_maxplayers
|
81
|
+
pug.maxplayers = new_maxplayers
|
82
|
+
"Max number of players set to #{pug.maxplayers} | #{pug.player_slots} joined"
|
83
|
+
else
|
84
|
+
"Current max number of players is #{pug.maxplayers} | #{pug.player_slots} joined"
|
85
|
+
end
|
76
86
|
|
77
|
-
|
87
|
+
send_and_log_message(message, e.channel)
|
78
88
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
89
|
+
if pug.full?
|
90
|
+
message = start_pug(
|
91
|
+
pug.player_slots,
|
92
|
+
e.mentions_for(pug.joined_players)
|
93
|
+
)
|
83
94
|
|
84
|
-
|
85
|
-
|
95
|
+
send_and_log_message(message, e.channel)
|
96
|
+
end
|
86
97
|
end
|
87
98
|
end
|
88
99
|
|
89
100
|
bot.command :leave do |event, *args|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
101
|
+
set_pug(event) do |e, pug|
|
102
|
+
if !pug.active?
|
103
|
+
message = "There's no active PUG to leave"
|
104
|
+
send_and_log_message(message, e.channel)
|
105
|
+
elsif !pug.joined_players.include?(e.user_id)
|
106
|
+
message = "You're not in the PUG"
|
107
|
+
send_and_log_message(message, e.channel)
|
108
|
+
else
|
109
|
+
pug.leave(e.user_id)
|
110
|
+
message = "#{e.display_name} leaves the PUG | #{pug.player_slots} remain"
|
111
|
+
send_and_log_message(message, e.channel)
|
112
|
+
|
113
|
+
if pug.empty?
|
114
|
+
message = end_pug(pug)
|
115
|
+
send_and_log_message(message, e.channel)
|
116
|
+
end
|
117
|
+
end
|
103
118
|
end
|
104
119
|
end
|
105
120
|
|
106
121
|
bot.command :end do |event, *args|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
122
|
+
set_pug(event) do |e, pug|
|
123
|
+
message = if !pug.active?
|
124
|
+
"There's no active PUG to end"
|
125
|
+
else
|
126
|
+
end_pug(pug)
|
127
|
+
end
|
111
128
|
|
112
|
-
|
113
|
-
|
129
|
+
send_and_log_message(message, e.channel)
|
130
|
+
end
|
114
131
|
end
|
115
132
|
|
116
|
-
bot.command :
|
117
|
-
|
118
|
-
|
133
|
+
bot.command :notify do |event, *args|
|
134
|
+
set_pug(event) do |e, pug|
|
135
|
+
roles = args.join(' ')
|
136
|
+
pug.notify_roles = roles
|
119
137
|
|
120
|
-
|
138
|
+
message = if roles.empty?
|
139
|
+
'Notification removed'
|
140
|
+
else
|
141
|
+
"Notification role set to #{roles}"
|
142
|
+
end
|
121
143
|
|
122
|
-
|
123
|
-
|
144
|
+
send_and_log_message(message, e.channel)
|
145
|
+
end
|
124
146
|
end
|
125
147
|
|
126
148
|
bot.run
|
@@ -128,16 +150,26 @@ class QwtfDiscordBotPug
|
|
128
150
|
|
129
151
|
private
|
130
152
|
|
131
|
-
def
|
132
|
-
|
133
|
-
|
153
|
+
def set_pug(event)
|
154
|
+
e = EventDecorator.new(event)
|
155
|
+
pug = Pug.for(e.channel_id)
|
156
|
+
yield(e, pug)
|
134
157
|
end
|
135
158
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
159
|
+
def start_pug(player_slots, mentions)
|
160
|
+
[
|
161
|
+
'Time to play!',
|
162
|
+
player_slots,
|
163
|
+
mentions.join(' ')
|
164
|
+
].join(' | ')
|
165
|
+
end
|
166
|
+
|
167
|
+
def end_pug(pug)
|
168
|
+
pug.end_pug
|
169
|
+
'PUG ended'
|
170
|
+
end
|
171
|
+
|
172
|
+
def send_and_log_message(message, channel)
|
173
|
+
channel.send_message(message) && puts(message)
|
142
174
|
end
|
143
175
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: qwtf_discord_bot
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.1.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sheldon Johnson
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-08-
|
11
|
+
date: 2020-08-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: discordrb
|
@@ -104,8 +104,9 @@ files:
|
|
104
104
|
- exe/qwtf_discord_bot
|
105
105
|
- lib/emoji.rb
|
106
106
|
- lib/endpoint.rb
|
107
|
-
- lib/
|
107
|
+
- lib/event_decorator.rb
|
108
108
|
- lib/player.rb
|
109
|
+
- lib/pug.rb
|
109
110
|
- lib/qstat_request.rb
|
110
111
|
- lib/qwtf_discord_bot.rb
|
111
112
|
- lib/qwtf_discord_bot/config.rb
|
data/lib/event_wrapper.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
class EventWrapper
|
2
|
-
include QwtfDiscordBot
|
3
|
-
|
4
|
-
DEFAULT_MAXPLAYERS = 8
|
5
|
-
|
6
|
-
def initialize(event)
|
7
|
-
@event = event
|
8
|
-
end
|
9
|
-
|
10
|
-
def user_id
|
11
|
-
@event.user.id
|
12
|
-
end
|
13
|
-
|
14
|
-
def username
|
15
|
-
@event.user.username
|
16
|
-
end
|
17
|
-
|
18
|
-
def maxplayers_key
|
19
|
-
[pug_key, "maxplayers"].join(":")
|
20
|
-
end
|
21
|
-
|
22
|
-
def maxplayers
|
23
|
-
redis.setnx(maxplayers_key, DEFAULT_MAXPLAYERS)
|
24
|
-
redis.get(maxplayers_key).to_i
|
25
|
-
end
|
26
|
-
|
27
|
-
def joined_player_count
|
28
|
-
redis.scard(players_key).to_i
|
29
|
-
end
|
30
|
-
|
31
|
-
def slots_left
|
32
|
-
maxplayers - joined_player_count
|
33
|
-
end
|
34
|
-
|
35
|
-
def pug_key
|
36
|
-
["pug", "channel", @event.channel.id].join(":")
|
37
|
-
end
|
38
|
-
|
39
|
-
def players_key
|
40
|
-
[pug_key, "players"].join(":")
|
41
|
-
end
|
42
|
-
|
43
|
-
def player_slots
|
44
|
-
"#{joined_player_count}/#{maxplayers}"
|
45
|
-
end
|
46
|
-
|
47
|
-
def users
|
48
|
-
@event.server.users
|
49
|
-
end
|
50
|
-
|
51
|
-
def role_key
|
52
|
-
[pug_key, "role"].join(":")
|
53
|
-
end
|
54
|
-
|
55
|
-
def role
|
56
|
-
redis.get(role_key) || "@here"
|
57
|
-
end
|
58
|
-
|
59
|
-
def pug_active?
|
60
|
-
redis.get(pug_key)
|
61
|
-
end
|
62
|
-
end
|