This repository has been archived by the owner on Feb 5, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 33
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Tim Morgan
committed
Jun 28, 2008
0 parents
commit e10fae9
Showing
57 changed files
with
6,203 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
require 'rake' | ||
require 'facets/symbol/to_proc' | ||
require 'facets/stylize' | ||
require 'libs/genesis' | ||
|
||
task :default do | ||
puts 'Type "rake --tasks" to see a list of tasks you can perform.' | ||
end | ||
|
||
# Load the Autumn environment. | ||
task :environment do | ||
AL_ROOT = File.dirname(__FILE__) | ||
@genesis = Autumn::Genesis.new | ||
@genesis.load_global_settings | ||
@genesis.load_season_settings | ||
end | ||
|
||
task :full_bootstrap do | ||
AL_ROOT = File.dirname(__FILE__) | ||
@genesis = Autumn::Genesis.new | ||
@genesis.boot! false | ||
end | ||
|
||
namespace :app do | ||
desc "Launch the Autumn daemon" | ||
task :start do | ||
system 'script/daemon start' | ||
end | ||
|
||
desc "Stop the Autumn daemon" | ||
task :stop do | ||
system 'script/daemon stop' | ||
end | ||
|
||
desc "Restart the Autumn daemon" | ||
task :restart do | ||
system 'script/daemon restart' | ||
end | ||
|
||
desc "Start Autumn but not as a daemon (stay on top)" | ||
task :run do | ||
system 'script/daemon run' | ||
end | ||
|
||
desc "Force the daemon to a stopped state (clears PID files)" | ||
task :zap do | ||
system 'script/daemon zap' | ||
end | ||
end | ||
|
||
namespace :log do | ||
desc "Remove all log files" | ||
task :clear do | ||
system 'rm -vf tmp/*.log tmp/*.output log/*.log*' | ||
end | ||
|
||
desc "Print all error messages in the log files" | ||
task :errors => :environment do | ||
season_log = "log/#{@genesis.config.global :season}.log" | ||
system_log = 'tmp/autumn-leaves.log' | ||
if File.exists? season_log then | ||
puts "==== ERROR-LEVEL LOG MESSAGES ====" | ||
File.open(season_log, 'r') do |log| | ||
puts log.grep(/^[EF],/) | ||
end | ||
end | ||
if File.exists? system_log then | ||
puts "==== UNCAUGHT EXCEPTIONS ====" | ||
File.open(system_log, 'r') do |log| | ||
puts log.grep(/^[EF],/) | ||
end | ||
end | ||
end | ||
end | ||
|
||
def local_db?(db) | ||
db.host.nil? or db.host == 'localhost' | ||
end | ||
|
||
namespace :db do | ||
desc "Create a database" | ||
task :create => :full_bootstrap do | ||
lname = ENV['LEAF'] | ||
raise "Usage: LEAF=[Leaf name] rake db:populate" unless lname | ||
raise "Unknown leaf #{lname}" unless leaf = Autumn::Foliater.instance.leaves[lname] | ||
raise "No databases configured" unless File.exist? "config/seasons/#{@genesis.config.global :season}/database.yml" | ||
db = DataMapper::Database[leaf.database_name] | ||
raise "No database configured for #{lname}" unless db | ||
|
||
case db.adapter.class.to_s | ||
when 'DataMapper::Adapters::MysqlAdapter' | ||
`echo "CREATE DATABASE #{db.database} CHARACTER SET utf8" | mysql -u#{db.username} -h#{db.host} -p#{db.password}` | ||
when 'DataMapper::Adapters::PostgresqlAdapter' | ||
local_db?(db) ? `createdb "#{db.database}" -E utf8` : raise("Can only create local PostgreSQL databases") | ||
when 'DataMapper::Adapters::Sqlite3Adapter' | ||
`sqlite3 "#{db.database}"` | ||
end | ||
end | ||
|
||
desc "Drop a database" | ||
task :drop => :full_bootstrap do | ||
lname = ENV['LEAF'] | ||
raise "Usage: LEAF=[Leaf name] rake db:populate" unless lname | ||
raise "Unknown leaf #{lname}" unless leaf = Autumn::Foliater.instance.leaves[lname] | ||
raise "No databases configured" unless File.exist? "config/seasons/#{@genesis.config.global :season}/database.yml" | ||
db = DataMapper::Database[leaf.database_name] | ||
raise "No database configured for #{lname}" unless db | ||
|
||
case db.adapter.class.to_s | ||
when 'DataMapper::Adapters::MysqlAdapter' | ||
`echo "DROP DATABASE #{db.database}" | mysql -u#{db.username} -h#{db.host} -p#{db.password}` | ||
when 'DataMapper::Adapters::PostgresqlAdapter' | ||
local_db?(db) ? `dropdb "#{db.database}"` : raise("Can only drop local PostgreSQL databases") | ||
when 'DataMapper::Adapters::Sqlite3Adapter' | ||
FileUtils.rm_f db.database | ||
end | ||
end | ||
|
||
desc "Create database tables according to the model objects" | ||
task :populate => :full_bootstrap do | ||
lname = ENV['LEAF'] | ||
raise "Usage: LEAF=[Leaf name] rake db:populate" unless lname | ||
raise "Unknown leaf #{lname}" unless leaf = Autumn::Foliater.instance.leaves[lname] | ||
|
||
leaf.database do | ||
Dir.glob("support/#{leaf.class.pathize}/**/*.rb").each do |file| | ||
content = nil | ||
File.open(file, 'r') { |f| content = f.read } | ||
content.scan(/class ([A-Z]\w+)/).flatten.each do |cname| | ||
klass = Module.const_get(cname.to_sym) | ||
next unless klass.ancestors.map(&:to_s).include? 'DataMapper::Base' | ||
puts "Creating table for #{cname}..." | ||
klass.table.create! | ||
end | ||
end | ||
end | ||
end | ||
|
||
desc "Drop, recreates, and repopulates a database" | ||
task :reset => [ 'db:drop', 'db:create', 'db:populate' ] | ||
end | ||
|
||
namespace :doc do | ||
desc "Generate API documentation for Autumn" | ||
task :api => [ :environment, :clear ] do | ||
system 'rm -rf doc/api' if File.directory? 'doc/api' | ||
system "rdoc --main README --title 'Autumn API Documentation' -o doc/api --line-numbers --inline-source libs README" | ||
end | ||
|
||
desc "Generate documentation for all leaves" | ||
task :leaves => [ :environment, :clear ] do | ||
system 'rm -rf doc/leaves' if File.directory? 'doc/leaves' | ||
system "rdoc --main README --title 'Autumn Leaves Documentation' -o doc/leaves --line-numbers --inline-source leaves support" | ||
end | ||
|
||
desc "Remove all documentation" | ||
task :clear => :environment do | ||
system 'rm -rf doc/api' if File.directory? 'doc/api' | ||
system 'rm -rf doc/leaves' if File.directory? 'doc/leaves' | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
season: testing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
--- | ||
Example: | ||
username: root | ||
adapter: mysql | ||
host: localhost | ||
database: example_database | ||
password: "" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
--- | ||
Scorekeeper: | ||
class: Scorekeeper |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--- | ||
logging: debug |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
--- | ||
Example: | ||
nick: Yournick | ||
leaves: | ||
- Scorekeeper | ||
rejoin: true | ||
channel: "#yourchannel" | ||
server: irc.yourircserver.com |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
require 'data_mapper' | ||
# Install the "chronic" gem for more robust date parsing | ||
begin | ||
gem 'chronic' | ||
require 'chronic' | ||
rescue Gem::LoadError | ||
end | ||
|
||
# An Autumn Leaf used for an in-channel scorekeeping system. This can operate | ||
# both as an open or closed score system. (In the former, new members are | ||
# automatically added when they receive points; in the latter, only authorized | ||
# members can give and receive points.) | ||
# | ||
# Scorekeeper is a database-backed leaf. It requires the DataMapper gem in order | ||
# to run. The database stores channels and their members, and each member's | ||
# point history. | ||
# | ||
# Scorekeeper supports pseudonyms. Entries in the +pseudonyms+ table can be used | ||
# to help ensure that the correct person's points are changed even when the | ||
# sender uses a nickname or abbreviation. | ||
# | ||
# This class contains only the methods directly relating to IRC. Other methods | ||
# are stored in ScorekeeperHelper and the model classes. | ||
# | ||
# = Usage | ||
# | ||
# !points [name]:: Get a person's score | ||
# !points [name] [+|-][number] [reason]:: Change a person's score (you must have a "+" or | ||
# a "-"). A reason is optional. | ||
# !points [name] history:: Return some recent history of that person's score. | ||
# !points [name] history [time period]:: Selects history from a time period. | ||
# !points [name] history [sender]:: Selects point changes from a sender. | ||
|
||
class Scorekeeper < Autumn::ChannelLeaf | ||
# Message displayed for !about command. | ||
ABOUT_MESSAGE = "Scorekeeper version 2.0 (2-29-08) by Tim Morgan: An Autumn Leaf." | ||
# Message displayed when a user uses incorrect !points syntax. | ||
USAGE = %[Examples: "!points", "!points Sancho +5", "!points Smith history", "!points Sancho history 2/27/08", "!points Sancho history Smith"] | ||
# Set this to true if you only want a specified set of users to receive and | ||
# give points. Set to false if anyone should be able to award points to | ||
# anyone. | ||
CLOSED_SYSTEM = false | ||
|
||
before_filter :authenticate, :only => [ :reload, :quit ] | ||
|
||
# Displays an about message. | ||
|
||
def about_command(stem, sender, reply_to, msg) | ||
ABOUT_MESSAGE | ||
end | ||
|
||
# Displays the current point totals, or modifies someone's score, depending on | ||
# the message provided with the command. | ||
|
||
def points_command(stem, sender, reply_to, msg) | ||
if msg.nil? or msg.empty? then | ||
totals(stem, reply_to) | ||
elsif msg =~ /^(\w+)\s+history\s*(.*)$/ then | ||
parse_history stem, reply_to, $1, $2 | ||
elsif msg =~ /^(\w+)\s+([\+\-]\d+)\s*(.*)$/ then | ||
parse_change stem, reply_to, sender, $1, $2.to_i, $3 | ||
else | ||
USAGE | ||
end | ||
end | ||
|
||
private | ||
|
||
def authenticate_filter(stem, channel, sender, command, msg, opts) | ||
# Returns true if the sender has any of the privileges listed below | ||
not ([ :operator, :admin, :founder, :channel_owner ] & [ stem.privilege(channel, sender) ].flatten).empty? | ||
end | ||
|
||
def points(stem, channel) | ||
chan = Channel.find_or_create :server => server_identifier(stem), :name => channel | ||
scores = Score.all(:channel_id.eql => chan.id) | ||
scores.inject(Hash.new(0)) { |hsh, score| hsh[score.receiver.name] += score.change; hsh } | ||
end | ||
|
||
def totals(stem, channel) | ||
if points(stem, channel).empty? then | ||
"No one has any points yet." | ||
else | ||
points(stem, channel).sort { |a,b| b.last <=> a.last }.collect { |n,p| "#{n}: #{p}" }.join(', ') | ||
end | ||
end | ||
|
||
def parse_change(stem, channel, sender, victim, delta, note) | ||
giver = find_person(stem, sender[:nick]) | ||
receiver = find_person(stem, victim) | ||
if giver.nil? and not CLOSED_SYSTEM then | ||
giver ||= Person.create :server => server_identifier(stem), :name => sender[:nick] | ||
giver.reload! # Get those default fields filled out | ||
end | ||
if receiver.nil? and not CLOSED_SYSTEM then | ||
receiver ||= Person.create :server => server_identifier(stem), :name => find_in_channel(stem, channel, victim) | ||
receiver.reload! # Get those default fields filled out | ||
end | ||
return "You can't change #{victim}'s points." unless authorized?(giver, receiver) | ||
change_points stem, channel, giver, receiver, delta, note | ||
return announce_change(giver, receiver, delta) | ||
end | ||
|
||
def parse_history(stem, channel, subject, argument) | ||
date = argument.empty? ? nil : parse_date(argument) | ||
scores = Array.new | ||
|
||
chan = Channel.first(:name.eql => channel) | ||
person = find_person(stem, subject) | ||
return "#{subject} has no points history." unless person | ||
|
||
if date then | ||
start, stop = find_range(date) | ||
scores = Score.all(:channel_id.eql => chan.id, :receiver_id.eql => person.id, :created_at.gte => start, :created_at.lt => stop, :order => 'created_at DESC') | ||
elsif argument.empty? then | ||
scores = Score.all(:channel_id.eql => chan.id, :receiver_id.eql => person.id, :order => 'created_at DESC') | ||
else | ||
giver = find_person(stem, argument) | ||
return "#{argument} has not given any points." unless giver | ||
scores = Score.all(:channel_id.eql => chan.id, :receiver_id.eql => person.id, :giver_id => giver.id, :order => 'created_at DESC') | ||
end | ||
return "No point history found." if scores.empty? | ||
|
||
str = String.new | ||
if scores.size > 5 then | ||
str << "(#{scores.size} point changes found; showing the first 5.)\n" | ||
scores = scores.slice(0, 5) | ||
end | ||
scores.each { |score| str << "[#{score.created_at.strftime '%m/%d %I:%M %p'}] #{score.giver.name} #{score.change > 0 ? 'gave' : 'docked'} #{score.receiver.name} #{score.change.abs.pluralize('point')}#{score.note ? (': ' + score.note) : ''}\n"} | ||
return str | ||
end | ||
end |
Oops, something went wrong.