<commit_before>
#
# Terminus-Bot: An IRC bot to solve all of the problems with IRC bots.
# Copyright (C) 2011 Terminus-Bot Development Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#


# If we're generating chains with random words, how
# many times do we try before giving up?
MAX_TRIES = 100

# Each word is a node. Each node contains a hash table of links to other nodes.
# A link is created each time one word follows another.
Node = Struct.new(:word, :links)

# Links are used to associate two nodes. The score is the number of times one
# word (represented by the target node) has followed the previous word (the
# parent node).
Link = Struct.new(:parent, :target, :score)


MARKOV_FILE = Terminus_Bot::DATA_DIR + "markov.db"

def initialize
  register_script("Markov chain implementation that generates somewhat readable text.")

  register_event("PRIVMSG", :on_privmsg)

  register_command("markov", :cmd_markov, 1, 10, "Manage the Markov script. Parameters: ON|OFF|FREQUENCY percentage|CLEAR|LOAD filename|INFO|GENERATE [word]")
  register_command("chain",  :cmd_chain,  0,  0, "Generate a random Markov chain.")

  @nodes = Hash.new

  read_database
end

def die
  write_database

  unregister_script
  unregister_events
  unregister_commands
end

def cmd_chain(msg, params)
  msg.reply(random_chain, false)
end

def cmd_markov(msg, params)
  arr = params[0].split
  here = msg.connection.name + "." + msg.destination

  case arr.shift.upcase

  when "ON"
    if msg.private?
      msg.reply("This command may only be used in channels.")
      return
    end

    store_data(here, true)

    msg.reply("Markov interaction enabled for this channel.")

  when "OFF"
    if msg.private?
      msg.reply("This command may only be used in channels.")
      return
    end

    store_data(here, false)

    msg.reply("Markov interaction disabled for this channel.")

  when "FREQUENCY"
    if msg.private?
      msg.reply("This command may only be used in channels.")
      return
    end

    unless arr.length == 1
      msg.reply("Please provide only one parameter for the FREQUENCY action.")
      return
    end

    chance = arr[0].to_i

    if chance <= 0 or chance > 100
      msg.reply("The frequency must be a positive whole number greater than 0 and less than or equal to 100.")
      return
    end

    store_data(:freq, chance)

    msg.reply("Frequency changed to #{chance}")

  when "CLEAR"

    @nodes.clear

    msg.reply("Working data set has been cleared.")

  when "LOAD"

    if arr.length == 0
      msg.reply("Please provide a list of files with the LOAD action.")
      return
    end

    msg.reply("Loading file(s). This may take a while.")

    Thread.pass

    read_files(msg, arr)

    msg.reply("Files loaded!")

  when "INFO"

    msg.reply("Items in data set: #{@nodes.length}")

  when "WRITE"

    begin
      write_database

      msg.reply("Database written.")
    rescue => e
      msg.reply("Failed to write database: #{e}")

      $log.error("markov.write_database") { e }
      $log.debug("markov.write_database") { e.backtrace }
    end

  when "GENERATE"

    if @nodes.length == 0
      msg.reply("There is no data from which to create a message.")
      return
    end

    chain = ""

    if arr.length >= 1
      chain = create_chain(arr.shift.downcase)
    else
      chain = random_chain
    end

    if chain.empty?
      msg.reply("I was unable to generate a chain.")
    else
      msg.reply(chain)
    end

  else

    msg.reply("Unknown action. Parameters: ON|OFF|FREQUENCY percentage|CLEAR|LOAD filename|INFO|GENERATE [word]")

  end

end


# Event Callbacks

def on_privmsg(msg)
  return if msg.private?

  msg.text.gsub(/(\x0F|\x1D|\02|\03([0-9]{1,2})?)/, "")

  if msg.text =~ /\01ACTION (.+)\01/
    parse_line($1)
  elsif msg.text.include? "\01"
    return
  else
    parse_line(msg.text)
  end

  return if msg.silent?

  return unless get_data(msg.connection.name + "." + msg.destination, false)

  return unless rand(100) <= get_data(:freq, 0)

  chain = create_chain(msg.text.split.sample.downcase)

  return if chain.empty?

  msg.reply(chain, false)
end


# Markov Stuff

def random_chain<commit_msg>Strip colors in the markov script a little better.<commit_after>
#
# Terminus-Bot: An IRC bot to solve all of the problems with IRC bots.
# Copyright (C) 2011 Terminus-Bot Development Team
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#


# If we're generating chains with random words, how
# many times do we try before giving up?
MAX_TRIES = 100

# Each word is a node. Each node contains a hash table of links to other nodes.
# A link is created each time one word follows another.
Node = Struct.new(:word, :links)

# Links are used to associate two nodes. The score is the number of times one
# word (represented by the target node) has followed the previous word (the
# parent node).
Link = Struct.new(:parent, :target, :score)


MARKOV_FILE = Terminus_Bot::DATA_DIR + "markov.db"

def initialize
  register_script("Markov chain implementation that generates somewhat readable text.")

  register_event("PRIVMSG", :on_privmsg)

  register_command("markov", :cmd_markov, 1, 10, "Manage the Markov script. Parameters: ON|OFF|FREQUENCY percentage|CLEAR|LOAD filename|INFO|GENERATE [word]")
  register_command("chain",  :cmd_chain,  0,  0, "Generate a random Markov chain.")

  @nodes = Hash.new

  read_database
end

def die
  write_database

  unregister_script
  unregister_events
  unregister_commands
end

def cmd_chain(msg, params)
  msg.reply(random_chain, false)
end

def cmd_markov(msg, params)
  arr = params[0].split
  here = msg.connection.name + "." + msg.destination

  case arr.shift.upcase

  when "ON"
    if msg.private?
      msg.reply("This command may only be used in channels.")
      return
    end

    store_data(here, true)

    msg.reply("Markov interaction enabled for this channel.")

  when "OFF"
    if msg.private?
      msg.reply("This command may only be used in channels.")
      return
    end

    store_data(here, false)

    msg.reply("Markov interaction disabled for this channel.")

  when "FREQUENCY"
    if msg.private?
      msg.reply("This command may only be used in channels.")
      return
    end

    unless arr.length == 1
      msg.reply("Please provide only one parameter for the FREQUENCY action.")
      return
    end

    chance = arr[0].to_i

    if chance <= 0 or chance > 100
      msg.reply("The frequency must be a positive whole number greater than 0 and less than or equal to 100.")
      return
    end

    store_data(:freq, chance)

    msg.reply("Frequency changed to #{chance}")

  when "CLEAR"

    @nodes.clear

    msg.reply("Working data set has been cleared.")

  when "LOAD"

    if arr.length == 0
      msg.reply("Please provide a list of files with the LOAD action.")
      return
    end

    msg.reply("Loading file(s). This may take a while.")

    Thread.pass

    read_files(msg, arr)

    msg.reply("Files loaded!")

  when "INFO"

    msg.reply("Items in data set: #{@nodes.length}")

  when "WRITE"

    begin
      write_database

      msg.reply("Database written.")
    rescue => e
      msg.reply("Failed to write database: #{e}")

      $log.error("markov.write_database") { e }
      $log.debug("markov.write_database") { e.backtrace }
    end

  when "GENERATE"

    if @nodes.length == 0
      msg.reply("There is no data from which to create a message.")
      return
    end

    chain = ""

    if arr.length >= 1
      chain = create_chain(arr.shift.downcase)
    else
      chain = random_chain
    end

    if chain.empty?
      msg.reply("I was unable to generate a chain.")
    else
      msg.reply(chain)
    end

  else

    msg.reply("Unknown action. Parameters: ON|OFF|FREQUENCY percentage|CLEAR|LOAD filename|INFO|GENERATE [word]")

  end

end


# Event Callbacks

def on_privmsg(msg)
  return if msg.private?

  msg.text.gsub(/(\x0F|\x1D|\02|\03([0-9]{1,2}(,[0-9]{1,2})?)?/, "")

  if msg.text =~ /\01ACTION (.+)\01/
    parse_line($1)
  elsif msg.text.include? "\01"
    return
  else
    parse_line(msg.text)
  end

  return if msg.silent?

  return unless get_data(msg.connection.name + "." + msg.destination, false)

  return unless rand(100) <= get_data(:freq, 0)

  chain = create_chain(msg.text.split.sample.downcase)

  return if chain.empty?

  msg.reply(chain, false)
end


# Markov Stuff

def random_chain
{'fraction_non_alphanumeric': 0.10022873778332293, 'fraction_numerical': 0.010397171969224371, 'mean_word_length': 3.1479085812850367, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1630875', 'n_tokens_mistral': 3088, 'n_tokens_neox': 2895, 'n_words': 1065}