jabber_admin 0.1.2 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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 +53 -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 -13
  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 +17 -12
  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 +20 -1
  41. metadata +119 -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 = callable.call(meth, check_res_body: false,
57
+ user: uid, host: host, **args)
58
+ body = (200..299).cover?(res.code) ? JSON.parse(res.body) : nil
59
+ body.is_a?(Hash) ? body['content'] : body
60
+ rescue JabberAdmin::Error => e
61
+ # When ejabberd tells us there was no value, it does this the hard way
62
+ next if e.response.body.include? 'error_no_value_found_in_vcard'
63
+ # Same for the case when there is no vCard at all
64
+ next if e.response.body.include? 'error_no_vcard_found'
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