atheme-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in atheme-ruby.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Noxx
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # atheme-ruby
2
+ The gem was inspired by the one of [jameswritescode/atheme-ruby](https://github.com/jameswritescode/atheme-ruby/).
3
+ However, his gem use module-methods and thus does not allow concurrent connections within the same script.
4
+
5
+ ## Install
6
+
7
+ Currently, no official gem yet exists, so you need to clone the repository and build it yourself:
8
+
9
+ git clone git@github.com:Flauschbaellchen/atheme-ruby.git
10
+ cd atheme-ruby
11
+ gem build atheme-ruby.gemspec
12
+ gem install atheme-ruby-x.x.x.gem
13
+
14
+ Standalone script:
15
+
16
+ require 'atheme'
17
+
18
+ Bundler/Rails:
19
+
20
+ gem 'atheme-ruby', require: 'atheme'
21
+
22
+ And you're ready to rumble!
23
+
24
+ ## Usage
25
+
26
+ To instantiate, you can either pass the required arguments as options in a
27
+ hash, like so:
28
+
29
+ Atheme::Session.new(
30
+ protocol: "http",
31
+ hostname: "localhost",
32
+ port: 8080
33
+ )
34
+
35
+ and/or use the builder idiom:
36
+
37
+ Atheme::Session.new do |c|
38
+ c.protocol = "http"
39
+ c.hostname = "localhost"
40
+ c.port = 8080
41
+ end
42
+
43
+ If an option is missing, the default ones as stated above are used.
44
+
45
+ ### Login
46
+
47
+ The initial session uses an anonymous login which can be re-choosen by calling @session.anonymous! or by logging out.
48
+
49
+ To login with an account registered with NickServ use the following:
50
+
51
+ @session.login(username, password, ip="127.0.0.1") #=> you'll get a cookie on success
52
+ @session.logged_in? #=> true or false
53
+
54
+ The cookie is a random generated string from Atheme for your current login session and will be valid for one(1) hour or until the server is shut down or restarted.
55
+
56
+ You can relogin using the cookie by calling:
57
+
58
+ @session.relogin(cookie, user, ip="127.0.0.1")
59
+
60
+ which saves you from asking users everytime again for their passwords.
61
+
62
+ You may logout after you finished your work:
63
+
64
+ @session.logout
65
+
66
+ ### Service-Calls
67
+
68
+ This gem supports all service-bots of atheme, like chanserv, nickserv etc.
69
+ You can call any commands you want to perform like you do on IRC:
70
+
71
+ @session.chanserv.info('#opers') # like /msg chanserv info #opers
72
+ @session.chanserv.list # like /msg chanserv list
73
+
74
+ I think you're getting the point...
75
+ However, you can perform additional questions on these return values:
76
+
77
+ @session.chanserv.info('#opers').founder #=> #<Atheme::User ...>
78
+ @session.chanserv.info('#opers').founder.name #=> "Nick_Of_Founder"
79
+ @session.chanserv.info('#opers').registered #=> #<Date: 2013-05-13 ((2456426j,0s,0n),+0s,2299161j)>
80
+
81
+ Take a look into _lib/atheme/services/*_ to find available subcommands.
82
+
83
+ TODO
84
+ ----
85
+ * Tests!
86
+ * Docs!
87
+ * Add more parsers/subcommands to all kinds of services (pull requests welcome)
88
+ * Brainstorming: Catch API-Errors and handle them gracefully. Provide a #success? method to decide if the command was successfully executed or not. Need to handle chains like the ones above.
89
+
90
+ Contributing to atheme-ruby
91
+ ---------------------------
92
+
93
+ * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
94
+ * Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
95
+ * Fork the project.
96
+ * Start a feature/bugfix branch.
97
+ * Commit and push until you are happy with your contribution.
98
+ * Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
99
+ * Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
100
+
101
+ Copyright
102
+ ---------
103
+
104
+ Copyright (c) 2013 Noxx/Flauschbaellchen. See LICENSE for further details.
105
+
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'atheme/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "atheme-ruby"
8
+ spec.homepage = "http://github.com/Flauschbaellchen/atheme-ruby"
9
+ spec.license = "MIT"
10
+ spec.summary = %Q{Atheme-XMLRPC interface for Ruby}
11
+ spec.description = %Q{Provides a ruby interface for Atheme's XMLRPC API}
12
+ spec.email = ["noxx@penya.de"]
13
+ spec.authors = ["Noxx"]
14
+ spec.version = Atheme::VERSION
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
data/lib/atheme.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Atheme
2
+ end
3
+
4
+ require "xmlrpc/client"
5
+
6
+ require "atheme/helpers"
7
+ require "atheme/version"
8
+ require "atheme/session"
9
+ require "atheme/entity"
10
+ require "atheme/service"
11
+
@@ -0,0 +1,7 @@
1
+ module Atheme
2
+ class Channel < Entity
3
+ def fetch!
4
+ @session.chanserv.info(@token)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Atheme
2
+ class User < EntityBase
3
+ def fetch!
4
+ @session.nickserv.info(@token)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,56 @@
1
+ module Atheme
2
+ class EntityBase
3
+
4
+ attr_reader :token
5
+ def initialize(session, hash_or_token)
6
+ @session = session
7
+ if hash_or_token.kind_of?(Hash)
8
+ @updated = true
9
+ hash_or_token.each do |k, v|
10
+ self.instance_variable_set("@#{k}".to_sym, v)
11
+ define_singleton_method(k) { v }
12
+ end
13
+ else
14
+ @updated = false
15
+ @token = hash_or_token
16
+ end
17
+ end
18
+
19
+ def method_missing(meth, *args, &block)
20
+ super if @updated || !fetchable?
21
+ do_fetch!
22
+ self.send(meth, *args, &block)
23
+ end
24
+
25
+ def fetch!
26
+ raise "#{self} does not know how to update itself. Slap the developer!"
27
+ end
28
+
29
+ def do_fetch!
30
+ @updated = true
31
+ result = fetch!
32
+ result.instance_variables.each do |key|
33
+ next if [:@session, :@updated, :@token].include? key
34
+ v = result.instance_variable_get(key)
35
+ self.instance_variable_set(key, v)
36
+ define_singleton_method(key.to_s[1..-1].to_sym) { v }
37
+ end
38
+ end
39
+
40
+ def fetchable?
41
+ true
42
+ end
43
+
44
+ private :do_fetch!, :fetchable?
45
+ end
46
+
47
+ class Entity < EntityBase
48
+ def fetchable?
49
+ false
50
+ end
51
+ end
52
+ end
53
+
54
+ Dir[File.expand_path('../entities/*.rb', __FILE__)].each { |file|
55
+ require file
56
+ }
@@ -0,0 +1,14 @@
1
+ module Atheme::Helpers
2
+ def self.constantize(camel_cased_word)
3
+ names = camel_cased_word.split('::')
4
+ names.shift if names.empty? || names.first.empty?
5
+
6
+ constant = Object
7
+
8
+ names.each do |name|
9
+ constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
10
+ end
11
+
12
+ constant
13
+ end
14
+ end
@@ -0,0 +1,101 @@
1
+ module Atheme
2
+ class Service
3
+ @@parsers = Hash.new
4
+ @@tmp_commands = []
5
+ @@tmp_class = nil
6
+
7
+ class Parser
8
+ attr_accessor :responder
9
+ attr_reader :commands
10
+
11
+ def initialize
12
+ @commands = []
13
+ end
14
+
15
+ def register_command(command)
16
+ @commands << command
17
+ end
18
+ end
19
+
20
+ class Command
21
+ attr_reader :name, :block
22
+
23
+ def initialize(name, opts, &block)
24
+ @name = name
25
+ @block = block
26
+ @opts = opts
27
+ end
28
+
29
+ def call(session, raw_output)
30
+ @raw_output = raw_output
31
+ value = self.instance_eval(&@block)
32
+ return value unless @opts[:as] || value.nil?
33
+ @opts[:as].new(session, value)
34
+ end
35
+
36
+ private
37
+ def raw_output
38
+ @raw_output
39
+ end
40
+
41
+ def match(expression)
42
+ ematch = expression.match(raw_output)
43
+ ematch && ematch[1]
44
+ end
45
+ end
46
+
47
+
48
+ def self.inherited(klass)
49
+ class_name = klass.name.gsub('Atheme::', '')
50
+ Atheme::Session.class_eval <<-RUBY
51
+ def #{class_name.downcase}
52
+ @#{class_name.downcase} ||= #{klass.name}.new(self)
53
+ end
54
+ RUBY
55
+
56
+ @@parsers[class_name.downcase] ||= Hash.new
57
+ end
58
+
59
+ def self.parse(cmd, &block)
60
+ service = self.name.sub('Atheme::', '').downcase
61
+ @@parsers[service][cmd] = Atheme::Service::Parser.new
62
+ @@staged_parser = @@parsers[service][cmd]
63
+ yield if block_given?
64
+ end
65
+
66
+ def self.command(name, opts={}, &block)
67
+ @@staged_parser.register_command(Atheme::Service::Command.new(name, opts, &block))
68
+ end
69
+
70
+ def self.responds_with(atheme_class)
71
+ @@staged_parser.responder = atheme_class
72
+ end
73
+
74
+ def initialize(session)
75
+ @session = session
76
+ end
77
+
78
+ def method_missing(method, *args, &block)
79
+ raw_output = @session.service_call(service_name, method, *args)
80
+
81
+ response = {raw_output: raw_output}
82
+ parser = @@parsers.has_key?(service_name) && @@parsers[service_name][method]
83
+
84
+ return Atheme::Entity.new(@session, response) unless parser
85
+
86
+ parser.commands.each do |command|
87
+ response[command.name] = command.call(@session, raw_output)
88
+ end
89
+ parser.responder.new(@session, response) if parser.responder
90
+ end
91
+
92
+ private
93
+ def service_name
94
+ self.class.name.gsub('Atheme::', '').downcase
95
+ end
96
+ end
97
+ end
98
+
99
+ Dir[File.expand_path('../services/*.rb', __FILE__)].each { |file|
100
+ require file
101
+ }
@@ -0,0 +1,61 @@
1
+ module Atheme
2
+ class ChanServ < Service
3
+
4
+ parse :info do
5
+ responds_with Atheme::Channel
6
+
7
+ command :name do
8
+ match(/^Information\son\s([&#+][^:]+):$/)
9
+ end
10
+
11
+ command :founder, as: Atheme::User do
12
+ match(/Founder\s+:\s+(\w+)/)
13
+ end
14
+
15
+ command :successor, as: Atheme::User do
16
+ match(/Successor\s+:\s+\(none\)/) ? nil : match(/Successor\s+:\s+(\w+)/)
17
+ end
18
+
19
+ command :registered do
20
+ Date.parse(match(/Registered\s+:\s+(\w+ [0-9]{2} [0-9(:?)]+ [0-9]{4})/))
21
+ end
22
+
23
+ command :last_used do
24
+ Date.parse(match(/Last\sused\s+:\s+(\w+ [0-9]{2} [0-9(:?)]+ [0-9]{4})/))
25
+ end
26
+
27
+ command :mode_lock do
28
+ match(/Mode\slock\s+:\s+([-+A-Za-z0-9]*)/)
29
+ end
30
+
31
+ command :entry_msg do
32
+ match(/Entrymsg\s+:\s+(.+)/)
33
+ end
34
+
35
+ command :flags do
36
+ flags = match(/Flags\s+:\s+(\w+(?:\s\w+)*)$/)
37
+ flags && flags.split || []
38
+ end
39
+
40
+ command :prefix do
41
+ match(/Prefix\s+:\s+([^\s])/)
42
+ end
43
+ end
44
+
45
+ parse :list do
46
+ command :to_a do
47
+ output = raw_output.split("\n")
48
+ output.delete_at(0)
49
+ output.delete_at(output.length - 1)
50
+
51
+ output.map do |info|
52
+ out = info.sub('- ', '').split('(')
53
+ channel = out[0].sub(' ', '')
54
+ owner = out[1].sub(')', '').sub(' ', '')
55
+
56
+ { channel: channel, owner: owner }
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class GameServ < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class Global < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class GroupServ < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class HelpServ < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class HostServ < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class InfoServ < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class MemoServ < Service
3
+ end
4
+ end
@@ -0,0 +1,68 @@
1
+ module Atheme
2
+ class NickServ < Service
3
+
4
+ parse :info do
5
+ responds_with Atheme::User
6
+
7
+ command :name do
8
+ match(/^Information\son\s([^\s]+)/)
9
+ end
10
+
11
+ command :account, as: Atheme::User do
12
+ match(/\(account\s([^\(]+)\):/)
13
+ end
14
+
15
+ command :registered do
16
+ Date.parse(match(/Registered\s+:\s+(\w+ [0-9]{2} [0-9(:?)]+ [0-9]{4})/))
17
+ end
18
+
19
+ command :entity_id do
20
+ match(/Entity\sID\s+:\s+([A-F0-9]+)$/)
21
+ end
22
+
23
+ command :vhost do
24
+ match(/vHost\s+:\s+([^\s]+)$/)
25
+ end
26
+
27
+ command :real_address do
28
+ match(/Real\saddr\s+:\s+([^\s]+)$/)
29
+ end
30
+
31
+ command :last_seen do
32
+ Date.parse(match(/Last\sseen\s+:\s+(\w+ [0-9]{2} [0-9(:?)]+ [0-9]{4})/))
33
+ end
34
+
35
+ command :user_seen do
36
+ time = match(/User\sseen\s+:\s+(\w+ [0-9]{2} [0-9(:?)]+ [0-9]{4})/)
37
+ time && Date.parse(time)
38
+ end
39
+
40
+ command :nicks do
41
+ nicks = match(/Nicks\s+:\s+([^\s]+(?:\s[^\s]+)*)$/)
42
+ nicks && nicks.split || []
43
+ end
44
+
45
+ command :email do
46
+ match(/Email\s+:\s+([^\s]+)/)
47
+ end
48
+
49
+ command :language do
50
+ match(/Language\s+:\s+([\w]+)$/)
51
+ end
52
+
53
+ command :flags do
54
+ flags = match(/Flags\s+:\s+(\w+(?:\s\w+)*)$/)
55
+ flags && flags.split || []
56
+ end
57
+
58
+ command :protected do
59
+ match(/has\s(enabled)\snick\sprotection/) ? true : false
60
+ end
61
+
62
+ command :groups do
63
+ flags = match(/Groups\s+:\s+(.+)$/)
64
+ flags && flags.split || []
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class OperServ < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class RPGServ < Service
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Atheme
2
+ class StatServ < Service
3
+ end
4
+ end
@@ -0,0 +1,87 @@
1
+ module Atheme
2
+ class Session
3
+
4
+ DEFAULT_CONNECTION_SETTINGS = {
5
+ protocol: 'http',
6
+ hostname: 'localhost',
7
+ port: '8080'
8
+ }
9
+ DEFAULT_IP = '127.0.0.1'
10
+
11
+ def initialize(opts={})
12
+ opts.each_key do |k|
13
+ if self.respond_to?("#{k}=")
14
+ self.send("#{k}=", opts[k])
15
+ else
16
+ raise ArgumentError, "Argument '#{k}' is not allowed"
17
+ end
18
+ end
19
+
20
+ yield self if block_given?
21
+
22
+ self.anonymous!
23
+ end
24
+
25
+ def login(user, password, ip=DEFAULT_IP)
26
+ return true if logged_in?
27
+ @cookie = self.call("atheme.login", user, password, ip)
28
+ @user, @ip = user, ip
29
+ @cookie
30
+ end
31
+
32
+ def relogin(cookie, user, ip=DEFAULT_IP)
33
+ @cookie, @user, @ip = cookie, user, ip
34
+ end
35
+
36
+ def logout
37
+ return true unless logged_in?
38
+ self.call("atheme.logout", @cookie, @user, @ip)
39
+ @cookie, @user, @ip = '.', '.', DEFAULT_IP
40
+ true
41
+ end
42
+ alias_method :anonymous!, :logout
43
+
44
+ def logged_in?
45
+ @cookie && @cookie!='.' ? true : false
46
+ end
47
+
48
+ def call(*args)
49
+ @server ||= connect_client
50
+ @server.call(*args)
51
+ end
52
+
53
+ def service_call(service, method, *args)
54
+ self.call("atheme.command", @cookie, @user, @ip, service, method, *args)
55
+ end
56
+
57
+ def protocol=(p)
58
+ @protocol = p
59
+ end
60
+
61
+ def hostname=(h)
62
+ @hostname = h
63
+ end
64
+
65
+ def port=(p)
66
+ @port = p.to_i
67
+ end
68
+
69
+ def protocol
70
+ @protocol || DEFAULT_CONNECTION_SETTINGS[:protocol]
71
+ end
72
+
73
+ def hostname
74
+ @hostname || DEFAULT_CONNECTION_SETTINGS[:hostname]
75
+ end
76
+
77
+ def port
78
+ @port || DEFAULT_CONNECTION_SETTINGS[:port]
79
+ end
80
+
81
+ private
82
+
83
+ def connect_client
84
+ @server = XMLRPC::Client.new2("#{self.protocol}://#{self.hostname}:#{self.port}/xmlrpc")
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,3 @@
1
+ module Atheme
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: atheme-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Noxx
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Provides a ruby interface for Atheme's XMLRPC API
47
+ email:
48
+ - noxx@penya.de
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - LICENSE
56
+ - README.md
57
+ - Rakefile
58
+ - atheme-ruby.gemspec
59
+ - lib/atheme.rb
60
+ - lib/atheme/entities/channel.rb
61
+ - lib/atheme/entities/user.rb
62
+ - lib/atheme/entity.rb
63
+ - lib/atheme/helpers.rb
64
+ - lib/atheme/service.rb
65
+ - lib/atheme/services/chanserv.rb
66
+ - lib/atheme/services/gameserv.rb
67
+ - lib/atheme/services/global.rb
68
+ - lib/atheme/services/groupserv.rb
69
+ - lib/atheme/services/helpserv.rb
70
+ - lib/atheme/services/hostserv.rb
71
+ - lib/atheme/services/infoserv.rb
72
+ - lib/atheme/services/memoserv.rb
73
+ - lib/atheme/services/nickserv.rb
74
+ - lib/atheme/services/operserv.rb
75
+ - lib/atheme/services/rpgserv.rb
76
+ - lib/atheme/services/statserv.rb
77
+ - lib/atheme/session.rb
78
+ - lib/atheme/version.rb
79
+ homepage: http://github.com/Flauschbaellchen/atheme-ruby
80
+ licenses:
81
+ - MIT
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ! '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 1.8.25
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: Atheme-XMLRPC interface for Ruby
104
+ test_files: []
105
+ has_rdoc: