<commit_before># encoding: utf-8
# This file is part of the K5 bot project.
# See files README.md and COPYING for copyright and licensing information.

# Clock plugin tells the time

require 'rubygems'
require 'bundler/setup'
require 'tzinfo'
require 'iso_country_codes'

require_relative '../../IRCPlugin'

class Clock < IRCPlugin
  Description = 'The Clock plugin tells the time.'
  Commands = {
      :time => "tells the current time. \
Optionally accepts space-separated list of timezone identifiers (e.g. Asia/Tokyo), \
or simply their parts after / and without spaces (e.g. 'NewYork'), \
timezone abbreviations (e.g. UTC, JST), \
ISO-3166 country codes (e.g. US, JP), \
and (single-word parts of) country names.",
      :jtime => 'tells the current time in Japan only',
      :utime => 'tells the current time in UTC only'
  }

  def normalize_zone_identifier(id)
    id.upcase.gsub(/_/,'').gsub(/-(\D)/,'\1')
  end

  def afterLoad
    # Try to gather all known zone abbreviations
    @zone_by_abbreviation = {}

    # For this we'll try to get periods for several dates,
    # To catch daylight-saving and other abbreviation changes.
    now = Time.now.utc
    half_year = 60*60*24*366/2
    times = [now - half_year, now, now + half_year]

    TZInfo::Timezone.all_data_zones.each do |zone|
      periods = []

      times.each do |time|
        periods |= zone.periods_for_local(time)
      end

      periods.each do |period|
        abbrev = period.abbreviation
        @zone_by_abbreviation[abbrev] ||= []
        @zone_by_abbreviation[abbrev] |= [zone]
      end
    end

    # Timezone.get() doesn't accept arbitrary case,
    # so let's compose our own case-insensitive table.
    @zone_by_identifier = {}
    @zone_by_city = {}

    TZInfo::Timezone.all.each do |zone|
      identifier = normalize_zone_identifier(zone.identifier)
      @zone_by_identifier.merge!(identifier => zone) do |key, old_val, new_val|
        raise "Can't disambiguate timezone #{key}: it's either #{old_val} or #{new_val}"
      end

      # The part after first slash.
      identifier.match(/^[^\/]+\/(.+)$/) do |m|
        @zone_by_city.merge!(m[1] => [zone]) do |_, old_val, new_val|
          old_val | new_val
        end
      end
    end
  end

  def beforeUnload
    @zone_by_abbreviation = nil
  end

  def on_privmsg(msg)
    case msg.bot_command
    when :time
      zones = (msg.tail || 'UTC JST').split
      time = Time.now
      msg.reply "#{time(time, zones)}"
    when :jtime
      time = Time.now
      msg.reply "#{time(time, %w(JST))}"
    when :utime
      time = Time.now
      msg.reply utime(time)
    when :new_year
      time = Time.now
      msg.reply(new_year_celebrators(time))
    end
  end

  def new_year_celebrators(time)
    time = Time.at(time).utc # convert time to UTC, or strftime won't work properly

    celebrating = TZInfo::Country.all.map do |country|
      has_new_year = country.zones.any? do |zone|
        '001 00'.eql?(zone.strftime('%j %H', time))
      end
      country.name if has_new_year
    end.reject(&:nil?)

    if celebrating.empty?
      'No countries celebrate at the moment'
    else
      celebrating.sort.join(',')
    end
  end

  def time(time, search_terms)
    time = Time.at(time).utc # convert time to UTC, or strftime won't work properly

    search_terms.map do |search_term|
      presorted = false

      # Try to search by abbreviation verbatim first.
      # Useful to differentiate e.g. 'MEST' vs 'MeST'.
      zones = @zone_by_abbreviation[search_term.to_sym]
      # If that didn't help, try upcase abbreviation.
      zones ||= @zone_by_abbreviation[search_term.upcase.to_sym]
      # Maybe it's a ISO 3166 country code, e.g. 'US'?
      unless zones<commit_msg>tmp Migrate Clock .new_year to LayoutableText<commit_after># encoding: utf-8
# This file is part of the K5 bot project.
# See files README.md and COPYING for copyright and licensing information.

# Clock plugin tells the time

require 'rubygems'
require 'bundler/setup'
require 'tzinfo'
require 'iso_country_codes'

require_relative '../../IRCPlugin'

class Clock < IRCPlugin
  Description = 'The Clock plugin tells the time.'
  Commands = {
      :time => "tells the current time. \
Optionally accepts space-separated list of timezone identifiers (e.g. Asia/Tokyo), \
or simply their parts after / and without spaces (e.g. 'NewYork'), \
timezone abbreviations (e.g. UTC, JST), \
ISO-3166 country codes (e.g. US, JP), \
and (single-word parts of) country names.",
      :jtime => 'tells the current time in Japan only',
      :utime => 'tells the current time in UTC only'
  }

  def normalize_zone_identifier(id)
    id.upcase.gsub(/_/,'').gsub(/-(\D)/,'\1')
  end

  def afterLoad
    # Try to gather all known zone abbreviations
    @zone_by_abbreviation = {}

    # For this we'll try to get periods for several dates,
    # To catch daylight-saving and other abbreviation changes.
    now = Time.now.utc
    half_year = 60*60*24*366/2
    times = [now - half_year, now, now + half_year]

    TZInfo::Timezone.all_data_zones.each do |zone|
      periods = []

      times.each do |time|
        periods |= zone.periods_for_local(time)
      end

      periods.each do |period|
        abbrev = period.abbreviation
        @zone_by_abbreviation[abbrev] ||= []
        @zone_by_abbreviation[abbrev] |= [zone]
      end
    end

    # Timezone.get() doesn't accept arbitrary case,
    # so let's compose our own case-insensitive table.
    @zone_by_identifier = {}
    @zone_by_city = {}

    TZInfo::Timezone.all.each do |zone|
      identifier = normalize_zone_identifier(zone.identifier)
      @zone_by_identifier.merge!(identifier => zone) do |key, old_val, new_val|
        raise "Can't disambiguate timezone #{key}: it's either #{old_val} or #{new_val}"
      end

      # The part after first slash.
      identifier.match(/^[^\/]+\/(.+)$/) do |m|
        @zone_by_city.merge!(m[1] => [zone]) do |_, old_val, new_val|
          old_val | new_val
        end
      end
    end
  end

  def beforeUnload
    @zone_by_abbreviation = nil
  end

  def on_privmsg(msg)
    case msg.bot_command
    when :time
      zones = (msg.tail || 'UTC JST').split
      time = Time.now
      msg.reply "#{time(time, zones)}"
    when :jtime
      time = Time.now
      msg.reply "#{time(time, %w(JST))}"
    when :utime
      time = Time.now
      msg.reply utime(time)
    when :new_year
      time = Time.now
      msg.reply(new_year_celebrators(time))
    end
  end

  def new_year_celebrators(time)
    time = Time.at(time).utc # convert time to UTC, or strftime won't work properly

    celebrating = TZInfo::Country.all.map do |country|
      has_new_year = country.zones.any? do |zone|
        '001 00'.eql?(zone.strftime('%j %H', time))
      end
      country.name if has_new_year
    end.reject(&:nil?)

    if celebrating.empty?
      'No countries celebrate at the moment'
    else
      LayoutableText::SimpleJoined.new(', ', celebrating.sort)
    end
  end

  def time(time, search_terms)
    time = Time.at(time).utc # convert time to UTC, or strftime won't work properly

    search_terms.map do |search_term|
      presorted = false

      # Try to search by abbreviation verbatim first.
      # Useful to differentiate e.g. 'MEST' vs 'MeST'.
      zones = @zone_by_abbreviation[search_term.to_sym]
      # If that didn't help, try upcase abbreviation.
      zones ||= @zone_by_abbreviation[search_term.upcase.to_sym]
      # Maybe it's a ISO 3166 country code, e.g. 'US'?
      unless zones
{'fraction_non_alphanumeric': 0.11491507430997877, 'fraction_numerical': 0.007165605095541401, 'mean_word_length': 2.774161241862794, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1277891', 'n_tokens_mistral': 2644, 'n_tokens_neox': 2477, 'n_words': 721}