artaius 0.2.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.
@@ -0,0 +1,2 @@
1
+ .rake_tasks~
2
+ Gemfile.lock
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source :rubygems
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Kyrylo Silin
2
+
3
+ This software is provided 'as-is', without any express or implied
4
+ warranty. In no event will the authors be held liable for any damages
5
+ arising from the use of this software.
6
+
7
+ Permission is granted to anyone to use this software for any purpose,
8
+ including commercial applications, and to alter it and redistribute it
9
+ freely, subject to the following restrictions:
10
+
11
+ 1. The origin of this software must not be misrepresented; you must not
12
+ claim that you wrote the original software. If you use this software
13
+ in a product, an acknowledgment in the product documentation would be
14
+ appreciated but is not required.
15
+
16
+ 2. Altered source versions must be plainly marked as such, and must not be
17
+ misrepresented as being the original software.
18
+
19
+ 3. This notice may not be removed or altered from any source distribution.
@@ -0,0 +1,19 @@
1
+ ![Artaius][logo]
2
+ ================
3
+
4
+ * [https://github.com/kyrylo/artaius/][rkag]
5
+
6
+ Description
7
+ -----------
8
+
9
+ Artaius is a [King Arthur's Gold][kag] (KAG) IRC bot. Currently it's not
10
+ suitable for a public use. Artaius dwells at #kag2d.ru@irc.quakenet.org.
11
+
12
+ License
13
+ -------
14
+
15
+ The project uses Zlib License. See LICENSE file for more information.
16
+
17
+ [logo]: http://img-fotki.yandex.ru/get/6210/98991937.9/0_7735e_6d44a25d_orig "Artaius logo"
18
+ [rkag]: https://github.com/kyrylo/artaius/ "Home page"
19
+ [kag]: http://kag2d.com/
@@ -0,0 +1,3 @@
1
+ FileList['tasks/*.rake'].each { |t| import t }
2
+
3
+ task :default => :test
@@ -0,0 +1,36 @@
1
+ require './lib/artaius/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'artaius'
5
+ s.version = Artaius::VERSION
6
+ s.date = Time.now.strftime('%Y-%m-%d')
7
+ s.summary = "IRC bot serving King Arthur's Gold players."
8
+ s.author = 'Kyrylo Silin'
9
+ s.email = 'kyrylosilin@gmail.com'
10
+ s.homepage = 'https://github.com/kyrylo/artaius'
11
+ s.license = 'zlib'
12
+
13
+ s.require_path = 'lib'
14
+ s.files = `git ls-files`.split "\n"
15
+ s.test_files = `git ls-files -- spec`.split "\n"
16
+
17
+ s.extra_rdoc_files = %W{README.md LICENSE}
18
+ s.rdoc_options = ["--charset=UTF-8"]
19
+
20
+ s.add_runtime_dependency 'cinch', '~>2.0'
21
+ s.add_runtime_dependency 'kag', '0.0.1'
22
+ s.add_runtime_dependency 'r18n-desktop', '>=0.4.14'
23
+ s.add_runtime_dependency 'sequel', '>=3.34.1'
24
+ s.add_runtime_dependency 'sqlite3', '>=1.3.6'
25
+
26
+ s.add_development_dependency 'rake'
27
+ s.add_development_dependency 'minitest', '>=2.12.1'
28
+ s.add_development_dependency 'fivemat', '>=1.0.0'
29
+
30
+ s.required_ruby_version = '~>1.9'
31
+
32
+ s.description = <<description
33
+ Artaius is an IRC (Internet Relay Chat) bot for KAG (King Arthur's Gold)
34
+ game. It cannot everything for the time being.
35
+ description
36
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require_relative '../lib/artaius'
4
+
5
+ Artaius::Bot.new.start
@@ -0,0 +1,40 @@
1
+ ---
2
+ identify:
3
+ send_challenge: "Identifying with Q, using CHALLENGEAUTH"
4
+ challenge: "[CHALLENGE received] Challenge: %1"
5
+
6
+ mixer:
7
+ players: "Players: %1."
8
+ need_players: !!pl
9
+ 1: "Need 1 more player to start the game."
10
+ n: "Need %1 more players to start the game"
11
+ cancel: "%1, you are not in the game anymore."
12
+ last_left: "The last player has left the game. The game is cancelled."
13
+ new_initiator: "New game inititator is %1."
14
+ roster: "Roster: %1."
15
+ n_slots_added: "%1 slots have been added. %2"
16
+ slot_added: "Slot added. %1"
17
+ n_slots_removed: "%1 slots have been removed. %2"
18
+ slot_removed: "Slot removed. %1"
19
+ slot_stats: "Slots: %1/%2."
20
+ blue_team: "Blue team: %1."
21
+ red_team: "Red team: %1."
22
+ glhf: "Good luck & have a good fun!"
23
+ slots_overall: "Total number of slots are %1."
24
+ game_cancelled: "The game has been cancelled due to shortage of players."
25
+ m:
26
+ game: "game"
27
+ play: "play"
28
+ cancel: "cancel"
29
+ roster: "roster"
30
+ start: "start"
31
+ slot: "slot"
32
+ slots: "slots"
33
+
34
+ archivarius:
35
+ exists: "%1, you are already registered and bonded your KAG account and IRC authname (%2)."
36
+ not_found: "Player %1 does not exist. Did you make a typing error?"
37
+ authname_required: "In order to bond your KAG account and IRC authname, you need to authorize on this server or register your nickname"
38
+ success: "Yay, registration complete! Enjoy KAG ;-)"
39
+ m:
40
+ reg: "reg"
@@ -0,0 +1,47 @@
1
+ ---
2
+ identify:
3
+ send_challenge: "Идентифицируюсь с помощью Q, используя CHALLENGEAUTH"
4
+ challenge: "[CHALLENGE получен] Challenge: %1"
5
+
6
+ mixer:
7
+ players: "Игроки: %1."
8
+ need_players: !!pl
9
+ 1: "Для начала игры нужен еще 1 игрок."
10
+ 2: "Для начала игры нужно еще %1 игрока."
11
+ n: "Для начала игры нужно еще %1 игроков."
12
+ cancel: "%1, вы вышли из игры."
13
+ last_left: "Последний игрок покинул игру. Игра отменена."
14
+ new_initiator: "%1 - новый инициатор игры."
15
+ roster: "Состав: %1."
16
+ n_slots_added: !!pl
17
+ 1: "Добавлен 1 слот. %2"
18
+ 2: "Добавлено %1 слота. %2"
19
+ n: "Добавлено %1 слотов. %2"
20
+ slot_added: "Слот добавлен. %1"
21
+ n_slots_removed: !!pl
22
+ 1: "Убран 1 слот. %2"
23
+ 2: "Убрано %1 слота. %2"
24
+ n: "Убрано %1 слотов. %2"
25
+ slot_removed: "Слот убран. %1"
26
+ slot_stats: "Слоты: %1/%2."
27
+ blue_team: "Команда синих: %1."
28
+ red_team: "Команда красных: %1."
29
+ glhf: "Удачи. Победит сильнейший!"
30
+ slots_overall: "Общее количество слотов: %1."
31
+ game_cancelled: "Игра была отменена из-за нехватки игроков."
32
+ m:
33
+ game: "игра"
34
+ play: "играть"
35
+ cancel: "отмена"
36
+ roster: "состав"
37
+ start: "старт"
38
+ slot: "слот"
39
+ slots: "слоты"
40
+
41
+ archivarius:
42
+ exists: "%1, вы уже привязали свой аккаунт в КАГ-е к вашему логину в ИРЦ (%2)."
43
+ not_found: "Игрок %1 не существует. Опечатались?"
44
+ authname_required: "Чтобы связать ваш аккаунт в КАГ-е и ИРЦ логин, вы должны авторизироваться на этом сервере или зарегистрировать свой ник."
45
+ success: "Ура, регистрация завершена! Наслаждайтеь КАГ-ом ;-)"
46
+ m:
47
+ reg: "регь"
@@ -0,0 +1 @@
1
+ identify.yml
@@ -0,0 +1 @@
1
+ *.db
@@ -0,0 +1,35 @@
1
+ # Create initial schema for the table of registered players.
2
+ #
3
+ # Note, that while KAG's maximum length of nicknames is equal to 20,
4
+ # QuakeNet allows only 15 letters (NICKLEN field in the ISUPPORT message).
5
+ #
6
+ # +--------------+--------------------------------------+
7
+ # | Field | Description |
8
+ # +--------------+--------------------------------------+
9
+ # | id | Player id number. |
10
+ # | irc_authname | IRC authname of player. |
11
+ # | kag_nick | King Arthur's Gold in-game nickname. |
12
+ # | role | Role of the player [1] |
13
+ # | premium | Account type (true if premium). |
14
+ # +--------------+--------------------------------------+
15
+ #
16
+ # [1] Roles:
17
+ # 0: Normal player account
18
+ # 1: KAG dev/team member
19
+ # 2: KAG Guard.
20
+ # 4: KAG team member ("admin" level, more or less the same as type 1)
21
+ # 5: Possibly a tester, but most testers are not currently flagged
22
+ #
23
+ Sequel.migration do
24
+ change do
25
+ create_table :players do
26
+ primary_key :id
27
+ String :irc_authname, :unique => true, :size => 15, :null => false
28
+ String :kag_nick, :unique => true, :size => 20, :null => false
29
+ Fixnum :role, :null => false
30
+ FalseClass :premium, :null => false
31
+ DateTime :created_at
32
+ DateTime :updated_at
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,16 @@
1
+ require 'cinch'
2
+ require 'kag'
3
+ require 'yaml'
4
+ require 'r18n-desktop'
5
+ require 'sequel'
6
+ require 'sequel/extensions/migration'
7
+
8
+ require_relative 'artaius/bot'
9
+ require_relative 'artaius/version'
10
+
11
+ require_relative 'artaius/database'
12
+ require_relative 'artaius/models/player'
13
+
14
+ require_relative 'artaius/plugins/identify'
15
+ require_relative 'artaius/plugins/mixer'
16
+ require_relative 'artaius/plugins/archivarius'
@@ -0,0 +1,50 @@
1
+ module Artaius
2
+
3
+ # Internal: Turn the bot into a polyglot.
4
+ I18n = R18n.from_env('config/locales/', :ru)
5
+
6
+ class Bot < Cinch::Bot
7
+
8
+ # Internal: Name.
9
+ FIRST_NAME = 'Artaius'
10
+
11
+ # Internal: Surname.
12
+ SECOND_NAME = 'Lucius'
13
+
14
+ # Internal: IRC server to join.
15
+ SERVER = 'irc.quakenet.org'
16
+
17
+ # Internal: Port of IRC server.
18
+ PORT = 6667
19
+
20
+ # Internal: Channels, that bot should be present on..
21
+ CHANNELS = ['#kag2d.ru']
22
+
23
+ def initialize
24
+ super
25
+
26
+ # Bot configuration.
27
+ configure do |c|
28
+ c.nick = FIRST_NAME
29
+ c.realname = "#{FIRST_NAME} #{SECOND_NAME}"
30
+ c.user = "#{FIRST_NAME} #{SECOND_NAME}"
31
+ c.server = SERVER
32
+ c.port = PORT
33
+ c.channels = CHANNELS
34
+ c.plugins.plugins = [
35
+ Artaius::Plugins::Identify,
36
+ Artaius::Plugins::Mixer,
37
+ Artaius::Plugins::Archivarius
38
+ ]
39
+
40
+ # Set up plugins to be used.
41
+ c.plugins.options[Artaius::Plugins::Identify] = {
42
+ :username => FIRST_NAME,
43
+ :password => Psych.load_file('config/plugins/identify.yml')[:password]
44
+ }
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+ end
@@ -0,0 +1,36 @@
1
+ module Artaius
2
+ # Internal: Connect Artaius to the database via Sequel interface.
3
+ Database = Sequel.sqlite(File.join(File.expand_path('db'), 'artaius.db'))
4
+
5
+ class << Database
6
+
7
+ # Internal: Migrate database from older version to the new one or vice
8
+ # versa. Without given arguments, migrate database to the latest version
9
+ # available.
10
+ #
11
+ # to - The Integer, describes version, to version till which migrations
12
+ # should be applied (default: nil).
13
+ # from - The Integer, describes version, from which migration should start
14
+ # its work (default: nil).
15
+ #
16
+ # Returns nothing.
17
+ def migrate(to = nil, from = nil)
18
+ migrations = File.join(File.expand_path('db'), 'migrations')
19
+ if to == 0 && from == 0
20
+ Sequel::Migrator.apply(self, migrations)
21
+ else
22
+ Sequel::Migrator.apply(self, migrations, to, from)
23
+ end
24
+ end
25
+
26
+ # Internal: Rollback current version of database to the previous one. This
27
+ # method returns database exactly one step back.
28
+ #
29
+ # Returns nothing
30
+ def rollback
31
+ current_version = self[:schema_info].first[:version]
32
+ migrate(current_version.pred)
33
+ end
34
+
35
+ end
36
+ end
@@ -0,0 +1,16 @@
1
+ module Artaius
2
+ # Internal: Represents KAG player.
3
+ class Player < Sequel::Model(:players)
4
+ plugin :timestamps, :update_on_create => true
5
+
6
+ # Internal: Check database for given authname.
7
+ #
8
+ # irc_authname - The String, represents IRC authname of the player.
9
+ #
10
+ # Returns true if the given authname is in database or false otherwise.
11
+ def self.exists?(irc_authname)
12
+ filter(:irc_authname => irc_authname).select(:irc_authname).any?
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,62 @@
1
+ module Artaius
2
+ module Plugins
3
+ # The plugin handles registration of users. The aim of the plugin is to
4
+ # associate KAG account and IRC authname together.
5
+ class Archivarius
6
+ include Cinch::Plugin
7
+
8
+ set react_on: :private
9
+
10
+ match /#{I18n.archivarius.m.reg} (\w{3,20})$/,
11
+ method: :sign_up,
12
+ use_suffix: false
13
+
14
+ # Internal: Sign up a new player with some restrictions: it won't sign up
15
+ # neither already registered players, nor players, that doesn't have IRC
16
+ # autname, nor nonexistent KAG players.
17
+ #
18
+ # m - The recieved message.
19
+ # kag_nick - The String, represents KAG nickname to be associated with
20
+ # caller's IRC authanme.
21
+ #
22
+ # Returns nothing.
23
+ def sign_up(m, kag_nick)
24
+ authname = m.user.authname
25
+ nick = m.user.nick
26
+
27
+ if authname.nil?
28
+ m.reply I18n.archivarius.authname_required and return
29
+ end
30
+
31
+ if already_exists?(authname)
32
+ m.reply I18n.archivarius.exists(nick, authname) and return
33
+ end
34
+
35
+ player = KAG::Player.new(kag_nick)
36
+
37
+ if player.info['statusMessage'] == 'Player not found'
38
+ m.reply I18n.archivarius.not_found(kag_nick) and return
39
+ end
40
+
41
+ Player.create(
42
+ :irc_authname => authname,
43
+ :kag_nick => player.username,
44
+ :role => player.role,
45
+ :premium => player.gold?
46
+ )
47
+
48
+ m.reply I18n.archivarius.success
49
+ end
50
+
51
+ protected
52
+
53
+ # Internal: Ask database for whether the IRC authname is in it or not.
54
+ #
55
+ # Returns true, if the IRC authname is there of false otherwise.
56
+ def already_exists?(irc_authname)
57
+ Player.exists?(irc_authname)
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,56 @@
1
+ require 'openssl'
2
+
3
+ module Artaius
4
+ module Plugins
5
+ # The plugin handles authentication via CHALLENGEAUTH. It requires two
6
+ # parameters: username and password of the bot. For details go here:
7
+ # http://www.quakenet.org/development/challengeauth/
8
+ class Identify
9
+ include Cinch::Plugin
10
+
11
+ set :required_options, [:username, :password]
12
+
13
+ # Internal: Q bot of QuakeNet. Nine times out of ten you write him a PM.
14
+ Q = 'Q@CServe.quakenet.org'
15
+
16
+
17
+ listen_to :connect,
18
+ method: :send_challenge
19
+
20
+ # Internal: Identify a bot with Q.
21
+ #
22
+ # Returns nothing.
23
+ def send_challenge(m)
24
+ debug I18n.identify.send_challenge
25
+ User(Q).privmsg('CHALLENGE')
26
+ end
27
+
28
+ match /^CHALLENGE (.+?) (.+)$/,
29
+ method: :challengeauth,
30
+ use_prefix: false,
31
+ use_suffix: false,
32
+ react_on: :notice
33
+
34
+ # Internal: Authenticate bot with safe CHALLENGEAUTH method.
35
+ #
36
+ # m - The recieved message.
37
+ # challenge - The CHALLENGE parameter, given by Q bot.
38
+ #
39
+ # Returns nothing.
40
+ def challengeauth(m, challenge)
41
+ # Q is the only trusted user.
42
+ return unless m.user && m.user.nick == 'Q'
43
+
44
+ debug I18n.identify.challenge(challenge)
45
+
46
+ username = config[:username].irc_downcase(:rfc1459)
47
+ password = config[:password][0, 10]
48
+
49
+ sha256 = OpenSSL::Digest::SHA256.new
50
+ key = sha256.hexdigest("#{ username }:#{ sha256.hexdigest(password) }")
51
+ response = OpenSSL::HMAC.hexdigest('SHA256', key, challenge)
52
+ User(Q).privmsg("CHALLENGEAUTH #{ username } #{ response } HMAC-SHA-256")
53
+ end
54
+ end
55
+ end
56
+ end