jabber_admin 0.1.1 → 1.0.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.
Files changed (42) hide show
  1. checksums.yaml +5 -5
  2. data/.editorconfig +30 -0
  3. data/.gitignore +4 -2
  4. data/.rubocop.yml +35 -0
  5. data/.simplecov +5 -0
  6. data/.travis.yml +8 -6
  7. data/.yardopts +4 -0
  8. data/CHANGELOG.md +49 -0
  9. data/Gemfile +4 -2
  10. data/Makefile +108 -0
  11. data/README.md +145 -21
  12. data/Rakefile +5 -3
  13. data/bin/config.rb +11 -0
  14. data/bin/console +3 -3
  15. data/docker-compose.yml +15 -0
  16. data/jabber_admin.gemspec +34 -27
  17. data/lib/jabber_admin.rb +97 -34
  18. data/lib/jabber_admin/api_call.rb +110 -20
  19. data/lib/jabber_admin/commands.rb +6 -12
  20. data/lib/jabber_admin/commands/ban_account.rb +11 -10
  21. data/lib/jabber_admin/commands/create_room.rb +15 -10
  22. data/lib/jabber_admin/commands/create_room_with_opts.rb +20 -14
  23. data/lib/jabber_admin/commands/get_vcard.rb +84 -0
  24. data/lib/jabber_admin/commands/muc_register_nick.rb +26 -0
  25. data/lib/jabber_admin/commands/register.rb +16 -11
  26. data/lib/jabber_admin/commands/registered_users.rb +18 -0
  27. data/lib/jabber_admin/commands/restart.rb +8 -5
  28. data/lib/jabber_admin/commands/send_direct_invitation.rb +19 -16
  29. data/lib/jabber_admin/commands/send_stanza.rb +21 -0
  30. data/lib/jabber_admin/commands/send_stanza_c2s.rb +28 -0
  31. data/lib/jabber_admin/commands/set_nickname.rb +22 -0
  32. data/lib/jabber_admin/commands/set_presence.rb +37 -0
  33. data/lib/jabber_admin/commands/set_room_affiliation.rb +26 -0
  34. data/lib/jabber_admin/commands/set_vcard.rb +68 -0
  35. data/lib/jabber_admin/commands/subscribe_room.rb +19 -12
  36. data/lib/jabber_admin/commands/unregister.rb +13 -7
  37. data/lib/jabber_admin/commands/unsubscribe_room.rb +10 -7
  38. data/lib/jabber_admin/configuration.rb +6 -1
  39. data/lib/jabber_admin/exceptions.rb +41 -0
  40. data/lib/jabber_admin/version.rb +19 -1
  41. metadata +120 -25
  42. data/lib/jabber_admin/initializer.rb +0 -5
@@ -2,17 +2,18 @@
2
2
 
3
3
  module JabberAdmin
4
4
  module Commands
5
- ##
6
- # Ban an account: kick sessions and set random password
7
- # https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#ban-account-ban-an-account-kick-sessions-and-set-random-password
5
+ # Ban an account by kicking sessions and set random password.
6
+ #
7
+ # @see https://bit.ly/2KW9xVt
8
8
  class BanAccount
9
- # @param [user] The user
10
- # @param [host] Server name
11
- # @param [reason] Reason for banning user
12
- def self.call(user:, host:, reason:)
13
- JabberAdmin::ApiCall.perform(
14
- 'ban_account', user: user, host: host, reason: reason
15
- )
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param user [String] user JID wo/ resource (eg. +tom@localhost+)
13
+ # @param reason [String] the banning reason (eg. +Spamming other users+)
14
+ def self.call(callable, user:, reason:)
15
+ uid, host = user.split('@')
16
+ callable.call('ban_account', user: uid, host: host, reason: reason)
16
17
  end
17
18
  end
18
19
  end
@@ -2,17 +2,22 @@
2
2
 
3
3
  module JabberAdmin
4
4
  module Commands
5
- ##
6
- # Create a MUC room name@service in host
7
- # https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#create-room-create-a-muc-room-name-service-in-host
5
+ # Create a new room (MUC).
6
+ #
7
+ # @see https://bit.ly/2rB8DFR
8
8
  class CreateRoom
9
- # @param [name] The room name
10
- # @param [service] MUC service
11
- # @param [host] Server host
12
- def self.call(name:, service:, host:)
13
- JabberAdmin::ApiCall.perform(
14
- 'create_room', name: name, service: service, host: host
15
- )
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # *Note:* this command should not be used in the bang-variant, due to raw
12
+ # result string in the response. (the response body is not zero/one like
13
+ # on almost all commands)
14
+ #
15
+ # @param callable [Proc, #call] the callable to call
16
+ # @param room [String] room JID (eg. +room1@conference.localhost+)
17
+ # @param host [String] the jabber host (eg. +localhost+)
18
+ def self.call(callable, room:, host:)
19
+ name, service = room.split('@')
20
+ callable.call('create_room', name: name, service: service, host: host)
16
21
  end
17
22
  end
18
23
  end
@@ -2,21 +2,27 @@
2
2
 
3
3
  module JabberAdmin
4
4
  module Commands
5
- ##
6
- # Create a MUC room name@service in host
7
- # https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#create-room-with-opts-create-a-muc-room-name-service-in-host-with-given-options
5
+ # Create a new room (MUC) with additional options.
6
+ #
7
+ # @see https://bit.ly/2IBEfVO
8
8
  class CreateRoomWithOpts
9
- # @param [name] The room name
10
- # @param [service] MUC service
11
- # @param [host] Server host
12
- # @param [options] [{ Name::String, Value::String }] List of options
13
- def self.call(name:, service:, host:, options:)
14
- JabberAdmin::ApiCall.perform(
15
- 'create_room_with_opts', name: name,
16
- service: service,
17
- host: host,
18
- options: options
19
- )
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param room [String] room JID (eg. +room1@conference.localhost+)
13
+ # @param host [String] the jabber host (eg. +localhost+)
14
+ # @param options pass additional +symbol: value+ pairs
15
+ def self.call(callable, room:, host:, **options)
16
+ name, service = room.split('@')
17
+ options = options.map do |key, value|
18
+ Hash['name', key.to_s, 'value', value.to_s]
19
+ end
20
+
21
+ callable.call('create_room_with_opts',
22
+ name: name,
23
+ service: service,
24
+ host: host,
25
+ options: options)
20
26
  end
21
27
  end
22
28
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JabberAdmin
4
+ module Commands
5
+ # Get content from a vCard.
6
+ #
7
+ # Examples:
8
+ #
9
+ # JabberAdmin.get_vcard!(
10
+ # :fn, 'n.given', 'org.orgunit[]', 'u.known[]',
11
+ # user: 'ac865680-9681-45da-8fee-8584053dde5b@jabber.local'
12
+ # )
13
+ # # => {:fn=>"Max Mustermann",
14
+ # # "n.given"=>"Max",
15
+ # # "org.orgunit"=>["Marketing", "Production"],
16
+ # # "u.known"=>nil}
17
+ #
18
+ # **Heads up!** ejabberd version 18.01 has a bug at the +get_vcard2_multi+
19
+ # command, which just returns the first element of possible multiple
20
+ # values. (in an array)
21
+ #
22
+ # @see https://bit.ly/2SLkEWi
23
+ # @see https://bit.ly/34T71dm
24
+ # @see https://bit.ly/3nKqiGL
25
+ class GetVcard
26
+ # Pass the correct data to the given callable.
27
+ #
28
+ # @param callable [Proc, #call] the callable to call
29
+ # @param keys [Array<String, Symbol>, String, Symbol] name of the
30
+ # vCard field (+n.family+ for multiple levels)
31
+ # @param user [String] user JID wo/ resource (eg. +tom@localhost+)
32
+ # @return [Hash] the vCard details
33
+ #
34
+ # rubocop:disable Metrics/MethodLength because the ejabberd REST API is
35
+ # hard to use in complex scenarios, so we have to work around it
36
+ # rubocop:disable Metrics/AbcSize dito
37
+ # rubocop:disable Metrics/CyclomaticComplexity dito
38
+ # rubocop:disable Metrics/PerceivedComplexity dito
39
+ def self.call(callable, *keys, user:)
40
+ uid, host = user.split('@')
41
+ val = proc do |key|
42
+ parts = key.to_s.upcase.split('.')
43
+ args = { name: parts.shift }
44
+ meth = 'get_vcard'
45
+
46
+ unless parts.empty?
47
+ args[:subname] = parts.shift
48
+ meth = 'get_vcard2'
49
+
50
+ if args[:subname].end_with? '[]'
51
+ meth += '_multi'
52
+ args[:subname].delete_suffix!('[]')
53
+ end
54
+ end
55
+
56
+ res = JSON.parse(callable.call(meth, check_res_body: false,
57
+ user: uid,
58
+ host: host,
59
+ **args).body)
60
+
61
+ res.is_a?(Hash) ? res['content'] : res
62
+ rescue JabberAdmin::CommandError => e
63
+ # When ejabberd tells us there was no value, it does this the hard way
64
+ next if e.response.body.include? 'error_no_value_found_in_vcard'
65
+
66
+ raise e
67
+ end
68
+
69
+ # When just one key is requested, we return the value directly
70
+ return val[keys.first] if keys.count == 1
71
+
72
+ # When multiple keys are requested, we assemble a hash
73
+ keys.map do |key|
74
+ res_key = key.is_a?(String) ? key.delete_suffix('[]') : key
75
+ [res_key, val[key]]
76
+ end.to_h
77
+ end
78
+ # rubocop:enable Metrics/MethodLength
79
+ # rubocop:enable Metrics/AbcSize
80
+ # rubocop:enable Metrics/CyclomaticComplexity
81
+ # rubocop:enable Metrics/PerceivedComplexity
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JabberAdmin
4
+ module Commands
5
+ # Register a nick to a user JID in the MUC service of a server.
6
+ #
7
+ # *Note*: On ejabberd <= 18.01 this command always returns a error code,
8
+ # even when the command was successful under the hood. (See:
9
+ # https://bit.ly/2L1CpvE)
10
+ #
11
+ # @see https://bit.ly/2G9EBNQ
12
+ class MucRegisterNick
13
+ # Pass the correct data to the given callable.
14
+ #
15
+ # @param callable [Proc, #call] the callable to call
16
+ # @param user [String] user JID wo/ resource (eg. +tom@localhost+)
17
+ # @param nick [String] the user nickname (eg. +TomTom+)
18
+ def self.call(callable, user:, nick:)
19
+ callable.call('muc_register_nick',
20
+ nick: nick,
21
+ jid: user,
22
+ serverhost: user.split('@').last)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -2,18 +2,23 @@
2
2
 
3
3
  module JabberAdmin
4
4
  module Commands
5
- ##
6
- # Register a user
7
- # https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#register-register-a-user
5
+ # Register a new user on the XMPP service.
6
+ #
7
+ # @see https://bit.ly/2wyhAox
8
8
  class Register
9
- # @param [user] The username
10
- # @param [host] Local vhost served by ejabberd
11
- # @param [password] The password
12
- def self.call(user:, host:, password:)
13
- JabberAdmin::ApiCall.perform(
14
- 'register',
15
- user: user, host: host, password: password
16
- )
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param user [String] user JID wo/ resource (eg. +tom@localhost+)
13
+ # @param password [String] the new plain password for the user
14
+ # (eg. +secret+)
15
+ def self.call(callable, user:, password:)
16
+ uid, host = user.split('@')
17
+ callable.call('register',
18
+ check_res_body: false,
19
+ user: uid,
20
+ host: host,
21
+ password: password)
17
22
  end
18
23
  end
19
24
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JabberAdmin
4
+ module Commands
5
+ # List all registered users on the ejabberd vhost.
6
+ #
7
+ # @see https://bit.ly/2KhwT6Z
8
+ class RegisteredUsers
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param host [String] the jabber vhost (eg. +localhost+)
13
+ def self.call(callable, host:)
14
+ callable.call('registered_users', check_res_body: false, host: host)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -2,12 +2,15 @@
2
2
 
3
3
  module JabberAdmin
4
4
  module Commands
5
- ##
6
- # Restart ejabberd gracefully
7
- # https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#restart-restart-ejabberd-gracefully
5
+ # Restart ejabberd service gracefully.
6
+ #
7
+ # @see https://bit.ly/2G7YEwd
8
8
  class Restart
9
- def self.call
10
- JabberAdmin::ApiCall.perform 'restart'
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ def self.call(callable)
13
+ callable.call('restart')
11
14
  end
12
15
  end
13
16
  end
@@ -2,23 +2,26 @@
2
2
 
3
3
  module JabberAdmin
4
4
  module Commands
5
- ##
6
- # Password and Message can also be: none. Users JIDs are separated with :
7
- # https://docs.ejabberd.im/developer/ejabberd-api/admin-api/#send-direct-invitation-send-a-direct-invitation-to-several-destinations
5
+ # Send a direct invitation to several destinations.
6
+ #
7
+ # @see https://bit.ly/2wuTpr2
8
8
  class SendDirectInvitation
9
- # @param [name] The room name
10
- # @param [service] MUC service
11
- # @param [password] Password, or none
12
- # @param [reason] The reason text, or none
13
- # @param [users] Users JIDs
14
- def self.call(name:, service:, password:, reason:, users:)
15
- joined_users = users.join(':')
16
-
17
- JabberAdmin::ApiCall.perform(
18
- 'send_direct_invitation',
19
- name: name, service: service, password: password, reason: reason,
20
- users: joined_users
21
- )
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param room [String] room JID (eg. +room1@conference.localhost+)
13
+ # @param users [Array<String>] user JIDs wo/ resource
14
+ # (eg. +tom@localhost+)
15
+ # @param password [String] a optional room password
16
+ # @param reason [String] the reason for the invitation
17
+ def self.call(callable, room:, users:, password: '', reason: '')
18
+ name, service = room.split('@')
19
+ callable.call('send_direct_invitation',
20
+ name: name,
21
+ service: service,
22
+ password: password,
23
+ reason: reason,
24
+ users: users.join(':'))
22
25
  end
23
26
  end
24
27
  end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JabberAdmin
4
+ module Commands
5
+ # Send a stanza; provide From JID and valid To JID.
6
+ #
7
+ # @see https://bit.ly/2rzxyK1
8
+ class SendStanza
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param from [String] user JID wo/ resource (eg. +tom@localhost+)
13
+ # @param to [String] user/room JID wo/ resource (eg. +bob@localhost+)
14
+ # @param stanza [String] the XML stanz to send
15
+ # (eg. <tt><message><ext attr='value'/></message></tt>)
16
+ def self.call(callable, from:, to:, stanza:)
17
+ callable.call('send_stanza', from: from, to: to, stanza: stanza)
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JabberAdmin
4
+ module Commands
5
+ # Send a message (stanza) as if sent from a c2s session.
6
+ #
7
+ # @see https://bit.ly/2wwUcYr
8
+ class SendStanzaC2s
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param user [String] user JID w/ resource (eg. +tom@localhost/bot+)
13
+ # @param stanza [String] the XML stanz to send
14
+ # (eg. <tt><message to='user1@localhost'>
15
+ # <ext attr='value'/></message></tt>)
16
+ def self.call(callable, user:, stanza:)
17
+ uid, host = user.split('@')
18
+ host, resource = host.split('/')
19
+ resource ||= 'bot'
20
+ callable.call('send_stanza_c2s',
21
+ user: uid,
22
+ host: host,
23
+ resource: resource,
24
+ stanza: stanza)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JabberAdmin
4
+ module Commands
5
+ # Set nickname in a user's vCard.
6
+ #
7
+ # @see https://bit.ly/2rBdyqc
8
+ class SetNickname
9
+ # Pass the correct data to the given callable.
10
+ #
11
+ # @param callable [Proc, #call] the callable to call
12
+ # @param user [String] user JID wo/ resource (eg. +tom@localhost+)
13
+ # @param nick [String] the user nickname (eg. +TomTom+)
14
+ def self.call(callable, user:, nick:)
15
+ uid, host = user.split('@')
16
+ callable.call(
17
+ 'set_nickname', user: uid, host: host, nickname: nick
18
+ )
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JabberAdmin
4
+ module Commands
5
+ # Send a stanza; provide From JID and valid To JID.
6
+ #
7
+ # @see https://bit.ly/2rzxyK1
8
+ class SetPresence
9
+ # @param callable [Proc, #call] the callable to call
10
+ # @param user [String] user JID w/ resource (eg. +tom@localhost/bot+)
11
+ # @param type [String] the presence type
12
+ # (eg. +available+, +error+, +probe+)
13
+ # @param show [String] the showed presence
14
+ # (eg. +away+, +chat+, +dnd+, +xa+)
15
+ # @param status [String] the user status (eg. +I'm online+)
16
+ # @param priority [String] presence priority (eg. +7+)
17
+ #
18
+ # rubocop:disable Metrics/ParameterLists because of the mapping
19
+ # rubocop:disable Metrics/MethodLength because of the mapping
20
+ def self.call(callable, user:, type: 'available', show: 'chat',
21
+ status: '', priority: '7')
22
+ uid, host = user.split('@')
23
+ host, resource = host.split('/')
24
+ resource ||= 'bot'
25
+ callable.call('set_presence',
26
+ user: uid,
27
+ host: host,
28
+ resource: resource,
29
+ type: type,
30
+ show: show,
31
+ status: status,
32
+ priority: priority)
33
+ end
34
+ # rubocop:enable Metrics/ParameterLists, Metrics/MethodLength
35
+ end
36
+ end
37
+ end