artaius 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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