2018-01-24 04:58:43 +00:00
#!/usr/bin/env ruby
require 'base64'
require 'digest'
2018-01-24 06:01:56 +00:00
require 'etc'
2020-02-08 16:16:47 +00:00
require 'json'
2018-01-28 01:16:09 +00:00
require 'readline'
2021-10-24 19:36:13 +00:00
require 'tempfile'
2020-02-08 16:16:47 +00:00
require 'time'
2018-02-13 02:43:55 +00:00
# require 'pry' # Only needed for debugging
2018-01-24 04:58:43 +00:00
2021-12-09 01:34:35 +00:00
class NilClass
def presence
self
end
end
class String
def presence
return nil if self . length == 0
self
end
end
2018-05-10 21:58:12 +00:00
class Config
2022-12-26 06:00:00 +00:00
VERSION = '1.1.2'
2018-05-10 21:58:12 +00:00
MESSAGE_FILE = " #{ ENV [ 'HOME' ] } /.iris.messages "
HISTORY_FILE = " #{ ENV [ 'HOME' ] } /.iris.history "
IRIS_SCRIPT = __FILE__
2021-12-09 01:34:35 +00:00
ENV_EDITOR = ENV [ 'EDITOR' ] . presence || ` which nano ` . chomp . presence
2018-05-10 21:58:12 +00:00
2020-02-08 17:04:07 +00:00
@@debug_mode = false
2022-12-21 04:13:32 +00:00
def self . hostname
return @hostname if @hostname
hostname = ` hostname ` . chomp
hostname = 'localhost' if hostname . empty?
return @hostname = hostname if hostname == 'localhost'
@hostname = hostname . split ( '.' ) [ - 2 .. - 1 ] . compact . join ( '.' )
end
def self . author
user = ENV [ 'USER' ] || ENV [ 'LOGNAME' ] || ENV [ 'USERNAME' ]
@author || = " #{ user } @ #{ self . hostname } "
end
2018-05-10 21:58:12 +00:00
def self . find_files
( ` ls /home/**/.iris.messages ` ) . split ( " \n " )
end
2020-02-08 16:16:47 +00:00
def self . messagefile_filename
$test_corpus_file || Config :: MESSAGE_FILE
end
def self . readfile_filename
" #{ messagefile_filename } .read "
end
2020-02-08 17:04:07 +00:00
def self . enable_debug_mode
@@debug_mode = true
end
def self . debug?
@@debug_mode
end
2018-05-10 21:58:12 +00:00
end
2018-02-06 15:30:01 +00:00
class String
2018-03-30 22:27:54 +00:00
COLOR_MAP = {
'n' = > '0' ,
'i' = > '1' ,
'u' = > '4' ,
'v' = > '7' ,
'r' = > '31' ,
'g' = > '32' ,
'y' = > '33' ,
'b' = > '34' ,
'm' = > '35' ,
'c' = > '36' ,
'w' = > '37' ,
}
COLOR_RESET = " \033 [0m "
def color_token
2018-04-01 00:43:20 +00:00
if self !~ / \ w /
return { '\{' = > '|KOPEN|' , '\}' = > '|KCLOSE|' , '}' = > COLOR_RESET } [ self ]
end
2018-03-30 22:27:54 +00:00
tag = self . scan ( / \ w / ) . map { | t | COLOR_MAP [ t ] } . sort . join ( ';' )
" \033 [ #{ tag } m "
end
def colorize
2018-04-01 00:43:20 +00:00
r = / \\ {|{[rgybmcwniuv]+ \ s| \\ }|} /
2018-03-30 22:27:54 +00:00
split = self . split ( r , 2 )
2020-03-07 22:59:37 +00:00
return self . color_bounded if r . match ( self ) . nil?
2018-03-30 22:27:54 +00:00
newstr = split . first + $& . color_token + split . last
2018-04-01 01:11:18 +00:00
if r . match ( newstr ) . nil?
2020-03-07 22:59:37 +00:00
return ( newstr + COLOR_RESET ) . gsub ( / \ |KOPEN \ | / , '{' ) . gsub ( / \ |KCLOSE \ | / , '}' ) . color_bounded
2018-04-01 01:11:18 +00:00
end
2018-03-30 22:27:54 +00:00
2020-03-07 22:59:37 +00:00
newstr . colorize . color_bounded
2018-03-30 22:27:54 +00:00
end
2018-04-01 00:43:20 +00:00
def decolorize
self .
gsub ( / \\ { / , '|KOPEN|' ) .
gsub ( / \\ } / , '|KCLOSE|' ) .
gsub ( / {[rgybmcwniuv]+ \ s|} / , '' ) .
gsub ( / \ |KOPEN \ | / , '{' ) .
gsub ( / \ |KCLOSE \ | / , '}' )
end
2018-02-06 15:30:01 +00:00
def wrapped ( width = Display :: WIDTH )
2018-04-01 01:11:18 +00:00
self . gsub ( / .{1, #{ width } }(?: \ s| \ Z| \ -) / ) {
( $& + 5 . chr ) . gsub ( / \ n \ 005 / , " \n " ) . gsub ( / \ 005 / , " \n " )
}
2018-02-06 15:30:01 +00:00
end
2018-02-07 03:22:08 +00:00
def pluralize ( count )
2018-04-01 01:11:18 +00:00
count == 1 ? self : self + 's'
2018-02-07 03:22:08 +00:00
end
2020-03-07 22:59:37 +00:00
def color_bounded
COLOR_RESET + self . gsub ( / \ n / , " \n #{ COLOR_RESET } " ) + COLOR_RESET
end
2018-02-06 15:30:01 +00:00
end
2018-02-02 05:59:57 +00:00
class Corpus
def self . load
2020-02-08 16:16:47 +00:00
if $test_corpus_file
@@corpus = IrisFile . load_messages
else
@@corpus = Config . find_files . map { | filepath | IrisFile . load_messages ( filepath ) } . flatten . sort_by ( & :timestamp )
end
2021-10-24 19:52:03 +00:00
@@my_corpus = IrisFile . load_messages . sort_by ( & :timestamp )
@@my_read_hashes = IrisFile . load_reads
2020-11-27 00:57:50 +00:00
@@unread_messages = nil
2018-02-02 05:59:57 +00:00
@@all_hash_to_index = @@corpus . reduce ( { } ) { | agg , msg | agg [ msg . hash ] = @@corpus . index ( msg ) ; agg }
2020-11-27 00:57:50 +00:00
@@edited_hashes = @@corpus . map ( & :edit_hash ) . compact
@@topics = @@corpus . select ( & :is_topic? )
2018-02-02 05:59:57 +00:00
@@all_parent_hash_to_index = @@corpus . reduce ( { } ) do | agg , msg |
agg [ msg . parent ] || = [ ]
agg [ msg . parent ] << @@corpus . index ( msg )
agg
end
end
2018-03-10 06:39:27 +00:00
def self . to_json
@@corpus . to_json
end
2020-11-27 00:57:50 +00:00
def self . edited_hashes
@@edited_hashes
2018-05-11 20:05:43 +00:00
end
2018-02-02 05:59:57 +00:00
def self . topics
@@topics
end
2021-10-24 19:52:03 +00:00
def self . authors
@@corpus . map ( & :author ) . uniq . sort
end
2018-02-02 05:59:57 +00:00
def self . mine
@@my_corpus
end
2018-05-11 20:05:43 +00:00
def self . is_mine? ( message )
@@my_corpus . map ( & :hash ) . include? message . hash
end
def self . index_of ( message )
@@corpus . map ( & :hash ) . index message . hash
end
def self . topic_index_of ( message )
@@topics . map ( & :hash ) . index message . hash
end
2018-02-02 05:59:57 +00:00
def self . find_message_by_hash ( hash )
return nil unless hash
index = @@all_hash_to_index [ hash ]
return nil unless index
2021-10-24 19:52:03 +00:00
@@corpus [ index ]
2018-05-11 20:05:43 +00:00
end
2018-02-02 05:59:57 +00:00
def self . find_all_by_parent_hash ( hash )
return [ ] unless hash
indexes = @@all_parent_hash_to_index [ hash ]
return [ ] unless indexes
2021-10-24 19:52:03 +00:00
indexes . map { | idx | @@corpus [ idx ] } . compact . select ( & :show_me? )
2018-02-02 05:59:57 +00:00
end
2018-02-13 02:43:55 +00:00
def self . find_topic_by_id ( topic_lookup )
2018-02-02 05:59:57 +00:00
return nil unless topic_lookup
2018-02-13 02:43:55 +00:00
index = topic_lookup . to_i - 1
2021-10-24 19:52:03 +00:00
@@topics [ index ] if index > = 0 && index < @@topics . length
2018-02-13 02:43:55 +00:00
end
2018-05-11 20:05:43 +00:00
def self . find_message_by_id ( message_lookup )
return nil unless message_lookup && message_lookup =~ / \ AM \ d+ \ Z /
index = message_lookup . gsub ( / M / , '' ) . to_i - 1
@@corpus [ index ] if index > = 0 && index < @@corpus . length
end
2018-02-13 02:43:55 +00:00
def self . find_topic_by_hash ( topic_lookup )
return nil unless topic_lookup
find_message_by_hash ( topic_lookup )
2018-01-24 06:01:56 +00:00
end
2018-02-07 03:21:44 +00:00
2018-02-07 03:22:08 +00:00
def self . unread_messages
2020-11-27 00:57:50 +00:00
@@unread_messages || = @@corpus
. select { | message | message . show_me? }
2021-10-24 19:52:03 +00:00
. reject { | m | @@my_read_hashes . include? m . hash }
2021-09-08 04:35:01 +00:00
. reject { | m | @@my_corpus . map ( & :hash ) . include? m . hash }
2020-11-27 00:57:50 +00:00
end
2018-02-07 03:22:08 +00:00
def self . unread_topics
2020-11-27 00:57:50 +00:00
@@topics . select do | m |
# Is the topic unread, or are any of its displayable replies unread?
m . unread? ||
find_all_by_parent_hash ( m . hash ) . reduce ( false ) { | agg , r | agg || r . unread? }
end
2018-02-07 03:22:08 +00:00
end
def self . size
@@corpus . size
end
2021-09-08 03:44:24 +00:00
def self . mark_as_read ( hashes )
2021-10-24 19:52:03 +00:00
new_reads = ( @@my_read_hashes + hashes ) . uniq . sort
2021-09-08 03:44:24 +00:00
IrisFile . write_read_file ( new_reads . to_json )
Corpus . load
end
2018-01-24 06:01:56 +00:00
end
class IrisFile
2020-02-08 16:16:47 +00:00
def self . load_messages ( filepath = nil )
if filepath . nil?
filepath = Config . messagefile_filename
end
2018-01-24 06:01:56 +00:00
return [ ] unless File . exists? ( filepath )
2018-01-31 05:24:36 +00:00
begin
payload = JSON . parse ( File . read ( filepath ) )
rescue JSON :: ParserError = > e
2020-02-08 16:16:47 +00:00
if filepath == Config . messagefile_filename
2018-02-08 18:11:44 +00:00
Display . flowerbox (
'Your message file appears to be corrupt.' ,
" Could not parse valid JSON from #{ filepath } " ,
'Please fix or delete this message file to use Iris.' )
2018-02-07 03:11:18 +00:00
exit ( 1 )
2018-01-31 05:24:36 +00:00
else
2018-02-08 18:11:44 +00:00
Display . say " * Unable to parse #{ filepath } , skipping... "
2018-01-31 05:24:36 +00:00
return [ ]
end
2020-06-06 21:56:32 +00:00
rescue Errno :: EACCES = > e
2020-11-27 00:57:50 +00:00
Display . warn " * Unable to read data from #{ filepath } , permission denied. Skipping... "
2020-06-06 21:56:32 +00:00
return [ ]
2018-01-31 05:24:36 +00:00
end
unless payload . is_a? ( Array )
2020-02-08 16:16:47 +00:00
if filepath == Config . messagefile_filename
2018-02-08 18:11:44 +00:00
Display . flowerbox (
'Your message file appears to be corrupt.' ,
" Could not interpret data from #{ filepath } " ,
'(It\'s not a JSON array of messages, as far as I can tell)' ,
'Please fix or delete this message file to use Iris.' )
2018-02-01 07:27:30 +00:00
exit ( 1 )
2018-01-31 05:24:36 +00:00
else
2018-02-08 18:11:44 +00:00
Display . say " * Unable to interpret data from #{ filepath } , skipping... "
2018-01-31 05:24:36 +00:00
return [ ]
end
end
2018-01-24 06:01:56 +00:00
uid = File . stat ( filepath ) . uid
2020-02-08 17:04:07 +00:00
begin
username = Etc . getpwuid ( uid ) . name
rescue ArgumentError
Display . warn ( " ' #{ filepath } ' does not appear to have a valid UID in /etc/passwd, skipping... " )
return [ ]
end
2018-01-24 06:01:56 +00:00
payload . map do | message_json |
new_message = Message . load ( message_json )
new_message . validate_user ( username )
new_message
2018-01-24 04:58:43 +00:00
end
end
2018-01-26 05:59:23 +00:00
2018-02-07 03:21:44 +00:00
def self . load_reads
2020-02-08 16:16:47 +00:00
return [ ] unless File . exists? Config . readfile_filename
2018-02-07 03:21:44 +00:00
begin
2020-02-08 16:16:47 +00:00
read_array = JSON . parse ( File . read ( Config . readfile_filename ) )
2018-02-07 03:21:44 +00:00
rescue JSON :: ParserError = > e
2018-02-08 18:11:44 +00:00
Display . flowerbox (
'Your read file appears to be corrupt.' ,
2020-02-08 16:16:47 +00:00
" Could not parse valid JSON from #{ Config . readfile_filename } " ,
2018-02-08 18:11:44 +00:00
'Please fix or delete this read file to use Iris.' )
2018-02-07 03:21:44 +00:00
exit ( 1 )
end
unless read_array . is_a? ( Array )
2018-02-08 18:11:44 +00:00
Display . flowerbox (
'Your read file appears to be corrupt.' ,
2020-02-08 16:16:47 +00:00
" Could not interpret data from #{ Config . readfile_filename } " ,
2018-02-08 18:11:44 +00:00
'(It\'s not a JSON array of message hashes, as far as I can tell)' ,
'Please fix or delete this read file to use Iris.' )
2018-02-07 03:21:44 +00:00
exit ( 1 )
end
read_array
end
2018-01-31 03:27:40 +00:00
def self . create_message_file
2020-02-08 16:16:47 +00:00
raise 'Should not try to create message file in test mode!' if $test_corpus_file
2018-02-07 03:11:18 +00:00
raise 'Message file exists; refusing to overwrite!' if File . exists? ( Config :: MESSAGE_FILE )
2018-01-31 03:27:40 +00:00
File . umask ( 0122 )
File . open ( Config :: MESSAGE_FILE , 'w' ) { | f | f . write ( '[]' ) }
end
2018-02-07 03:21:44 +00:00
def self . create_read_file
2020-02-08 16:16:47 +00:00
return if File . exists? ( Config . readfile_filename )
2018-02-07 03:21:44 +00:00
File . umask ( 0122 )
2020-02-08 16:16:47 +00:00
File . open ( Config . readfile_filename , 'w' ) { | f | f . write ( '[]' ) }
2018-02-07 03:21:44 +00:00
end
2018-01-26 05:59:23 +00:00
def self . write_corpus ( corpus )
2020-02-08 16:16:47 +00:00
File . write ( Config . messagefile_filename , corpus )
2018-01-26 05:59:23 +00:00
end
2018-02-07 03:21:44 +00:00
2018-02-07 03:30:46 +00:00
def self . write_read_file ( new_read_hashes )
2020-02-08 16:16:47 +00:00
if $test_corpus_file
File . write ( " #{ $test_corpus_file } .read " , new_read_hashes )
else
File . write ( Config . readfile_filename , new_read_hashes )
end
2018-02-07 03:21:44 +00:00
end
2018-01-26 05:59:23 +00:00
end
2018-01-24 04:58:43 +00:00
class Message
2018-05-13 03:34:30 +00:00
attr_reader :timestamp , :edit_hash , :author , :parent , :message , :errors , :is_deleted
2018-01-24 04:58:43 +00:00
2022-12-21 04:13:32 +00:00
def initialize ( message , parent = nil , author = Config . author , edit_hash = nil , timestamp = Time . now . utc . iso8601 , is_deleted = nil )
2018-05-13 03:34:30 +00:00
@message = message
@parent = parent
@author = author
@edit_hash = edit_hash
@timestamp = timestamp
@hash = hash
@is_deleted = is_deleted
@errors = [ ]
2018-01-24 04:58:43 +00:00
end
def self . load ( payload )
2018-01-24 06:01:56 +00:00
data = payload if payload . is_a? ( Hash )
data = JSON . parse ( payload ) if payload . is_a? ( String )
2018-01-24 04:58:43 +00:00
2018-05-13 03:34:30 +00:00
loaded_message = self . new ( data [ 'data' ] [ 'message' ] , data [ 'data' ] [ 'parent' ] , data [ 'data' ] [ 'author' ] , data [ 'edit_hash' ] , data [ 'data' ] [ 'timestamp' ] , data [ 'is_deleted' ] )
2018-01-24 06:01:56 +00:00
loaded_message . validate_hash ( data [ 'hash' ] )
2018-01-24 04:58:43 +00:00
loaded_message
end
2018-05-11 20:05:43 +00:00
def self . edit ( new_text , old_message )
2018-05-13 03:34:30 +00:00
Message . new ( new_text , old_message . parent , old_message . author , old_message . hash , old_message . timestamp ) . save!
end
2020-11-27 00:57:50 +00:00
def is_topic?
parent . nil? && show_me?
end
2018-05-13 03:34:30 +00:00
def delete
@is_deleted = ! @is_deleted
replace!
2018-05-11 20:05:43 +00:00
end
def edited?
! ( edit_hash . nil? || edit_hash . empty? )
end
2020-11-27 00:57:50 +00:00
# Only show messages that don't have a following, edited message
2018-05-11 20:05:43 +00:00
def show_me?
2020-11-27 00:57:50 +00:00
! Corpus . edited_hashes . include? ( hash )
2018-05-11 20:05:43 +00:00
end
2018-01-24 06:01:56 +00:00
def validate_user ( username )
@errors << 'Unvalidatable; could not parse username' if username . nil?
@errors << 'Unvalidatable; username is empty' if username . empty?
2020-03-07 22:50:37 +00:00
user_regex = Regexp . new ( " (.*)@.*$ " )
2018-01-31 05:24:36 +00:00
author_match = user_regex . match ( author )
unless author_match && author_match [ 1 ] == username
@errors << " Bad username: got #{ author } 's message from #{ username } 's message file. "
end
2018-01-24 06:01:56 +00:00
end
def validate_hash ( test_hash )
2018-01-31 01:25:56 +00:00
if self . hash != test_hash
2018-01-31 04:47:45 +00:00
@errors << " Broken hash: expected ' #{ hash . chomp } ', got ' #{ test_hash . chomp } ' "
2018-01-24 06:01:56 +00:00
end
end
def valid?
@errors . empty?
end
2018-05-13 03:34:30 +00:00
def replace!
new_corpus = Corpus . mine . reject { | message | message . hash == self . hash } << self
IrisFile . write_corpus ( JSON . pretty_generate ( new_corpus ) )
Corpus . load
end
2018-01-26 05:59:23 +00:00
def save!
new_corpus = Corpus . mine << self
2018-03-30 22:28:15 +00:00
IrisFile . write_corpus ( JSON . pretty_generate ( new_corpus ) )
2018-01-26 05:59:23 +00:00
Corpus . load
end
2018-01-24 05:13:59 +00:00
def hash ( payload = nil )
2018-01-31 01:25:56 +00:00
if payload . nil?
return @hash if @hash
payload = unconfirmed_payload . to_json
end
2018-01-24 05:13:59 +00:00
Base64 . encode64 ( Digest :: SHA1 . digest ( payload ) )
2018-01-24 04:58:43 +00:00
end
2018-05-13 03:34:30 +00:00
def truncated_display_message ( length )
if is_deleted
2018-05-13 04:57:12 +00:00
stub = '{r TOPIC DELETED BY AUTHOR}'
2018-05-13 03:34:30 +00:00
else
stub = message . split ( " \n " ) . first
end
return stub . colorize if stub . decolorize . length < = length
2020-02-08 16:16:47 +00:00
# Colorize the stub, then decolorize to strip out any partial tags
2018-05-13 03:34:30 +00:00
stub . colorize . slice ( 0 , length - 5 - Display . topic_index_width ) . decolorize + '...'
end
2018-01-28 01:16:09 +00:00
def truncated_message ( length )
2018-04-01 05:45:22 +00:00
stub = message . split ( " \n " ) . first
return stub . colorize if stub . decolorize . length < = length
2020-11-27 00:57:50 +00:00
# Colorize the stub, then decolorize to strip out any partial tags
2018-05-11 20:26:42 +00:00
stub . colorize . slice ( 0 , length - 5 - Display . topic_index_width ) . decolorize + '...'
2018-01-28 01:16:09 +00:00
end
2018-04-01 00:38:58 +00:00
def latest_topic_timestamp
2018-05-11 20:06:34 +00:00
( replies . map ( & :timestamp ) . max || timestamp || 'UNKNOWN' ) . gsub ( / T / , ' ' ) . gsub ( / Z / , '' )
2018-04-01 00:38:58 +00:00
end
2018-05-13 04:05:51 +00:00
def unread?
Corpus . unread_messages . include? self
end
def topic_status
return '{r X}' unless valid?
unread_count = replies . count ( & :unread? )
2018-05-13 05:42:04 +00:00
unread_count += 1 if self . unread?
2018-05-13 04:05:51 +00:00
return ' ' if unread_count == 0
return '*' if unread_count > 9
unread_count . to_s
end
2018-01-28 01:16:09 +00:00
def to_topic_line ( index )
2018-05-13 04:05:51 +00:00
head = [ Display . print_index ( index ) , topic_status , latest_topic_timestamp , Display . print_author ( author ) ] . join ( ' | ' )
2018-05-13 03:34:30 +00:00
message_stub = truncated_display_message ( Display :: WIDTH - head . decolorize . length - 1 )
2018-05-13 04:05:51 +00:00
'| ' + [ head , message_stub ] . join ( ' | ' )
2018-01-28 01:16:09 +00:00
end
2018-01-31 01:25:56 +00:00
def to_display
2018-05-13 04:06:47 +00:00
error_marker = valid? ? nil : '{r ### THIS MESSAGE HAS THE FOLLOWING ERRORS ###}'
error_follower = valid? ? nil : '{r ### THIS MESSAGE MAY BE CORRUPT OR MAY HAVE BEEN TAMPERED WITH ###}'
2018-05-11 20:21:26 +00:00
message_header = " #{ leader_text } On #{ timestamp } , #{ author } #{ verb_text } ... "
header_bar = ( indent_text + message_header + ( '-' * ( Display :: WIDTH ) ) )
header_offset = header_bar . length - header_bar . decolorize . length
2020-11-27 00:57:50 +00:00
header_bar = header_bar [ 0 .. Display :: WIDTH + header_offset - 1 ]
2018-02-06 15:30:01 +00:00
2018-04-01 00:43:20 +00:00
bar = indent_text + ( '-' * ( Display :: WIDTH - indent_text . decolorize . length ) )
2018-05-13 03:34:30 +00:00
if @is_deleted
message_text = nil
else
message_text = message . wrapped ( Display :: WIDTH - ( indent_text . decolorize . length + 1 ) ) . split ( " \n " ) . map { | m | indent_text + m } . join ( " \n " )
end
2018-01-28 01:16:09 +00:00
[
2018-02-01 23:43:04 +00:00
'' ,
2018-01-31 04:47:45 +00:00
error_marker ,
errors ,
error_follower ,
2018-05-11 20:21:26 +00:00
header_bar ,
2018-02-06 15:30:01 +00:00
message_text ,
bar
2018-01-31 04:47:45 +00:00
] . flatten . compact . join ( " \n " )
2018-01-28 01:16:09 +00:00
end
2018-01-31 01:25:56 +00:00
def to_topic_display
[ to_display ] + replies . map ( & :to_display )
end
2021-10-24 20:39:30 +00:00
# TODO: Is this only used for hashing? Maybe rename.
2018-01-26 05:59:23 +00:00
def to_json ( * args )
2018-01-24 05:13:59 +00:00
{
hash : hash ,
edit_hash : edit_hash ,
2018-05-13 03:34:30 +00:00
is_deleted : is_deleted ,
2018-01-24 05:13:59 +00:00
data : unconfirmed_payload
} . to_json
2018-01-24 04:58:43 +00:00
end
2018-05-13 05:41:40 +00:00
def edit_predecessor
return nil unless edit_hash
Corpus . find_message_by_hash ( edit_hash )
end
2020-11-27 00:57:50 +00:00
# Find all messages replying to the current topic, including replies to topics
# which have been edited.
2018-02-07 03:21:44 +00:00
def replies
2021-09-22 04:58:43 +00:00
all_replies = Corpus . find_all_by_parent_hash ( hash )
all_replies += ( ( edit_predecessor && edit_predecessor . replies ) || [ ] )
all_replies . compact . sort_by { | reply | Corpus . index_of ( reply ) }
2018-02-07 03:21:44 +00:00
end
2018-05-11 20:05:43 +00:00
def id
'M' + ( Corpus . index_of ( self ) + 1 ) . to_s
end
def topic_id
2021-10-24 19:52:03 +00:00
return nil unless self . is_topic?
2018-05-11 20:05:43 +00:00
Corpus . topic_index_of ( self ) + 1
end
2018-02-02 05:59:57 +00:00
private
2018-05-13 03:34:30 +00:00
def status_flag
2018-05-13 04:57:12 +00:00
return '{r (deleted)}' if @is_deleted
'{y (edited)}' if edited?
2018-05-11 20:05:43 +00:00
end
2018-02-02 05:59:57 +00:00
def leader_text
2021-10-24 20:39:30 +00:00
is_topic? ? " {g ***} [ #{ topic_id } ] #{ status_flag } " : [ " {g ===} " , " [ #{ id } ] " , status_flag ] . compact . join ( ' ' )
2018-02-02 05:59:57 +00:00
end
def verb_text
2021-10-24 20:39:30 +00:00
is_topic? ? 'posted' : 'replied'
2018-02-02 05:59:57 +00:00
end
2018-02-06 15:30:01 +00:00
def indent_text
2021-10-24 20:39:30 +00:00
is_topic? ? '' : ' | '
2018-02-06 15:30:01 +00:00
end
2018-01-24 04:58:43 +00:00
def unconfirmed_payload
2018-01-24 05:13:59 +00:00
{
author : author ,
parent : parent ,
timestamp : timestamp ,
message : message ,
}
2018-01-24 04:58:43 +00:00
end
end
2018-01-28 01:16:09 +00:00
class Display
2018-01-31 05:25:52 +00:00
MIN_WIDTH = 80
2020-11-27 00:57:50 +00:00
MIN_HEIGHT = 8
2018-01-31 05:25:52 +00:00
WIDTH = [ ENV [ 'COLUMNS' ] . to_i , ` tput cols ` . chomp . to_i , MIN_WIDTH ] . compact . max
2020-11-27 00:57:50 +00:00
HEIGHT = [ ENV [ 'ROWS' ] . to_i , ` tput lines ` . chomp . to_i , MIN_HEIGHT ] . compact . max
TITLE_WIDTH = WIDTH - 26
2018-01-28 01:16:09 +00:00
2018-05-31 16:17:32 +00:00
def self . permissions_error ( filename , file_description , permission_string , mode_string , consequence = nil )
message = [
" Your #{ file_description } file has incorrect permissions! Should be \" #{ permission_string } \" . " ,
" You can change this from the command line with: " ,
" chmod #{ mode_string } #{ filename } " ,
consequence
] . compact
self . flowerbox ( message )
end
2018-02-08 18:11:44 +00:00
def self . flowerbox ( * lines , box_character : '*' , box_thickness : 1 )
box_thickness . times do say box_character * WIDTH end
lines . each { | line | say line }
box_thickness . times do say box_character * WIDTH end
end
def self . say ( stuff = '' )
2018-03-30 22:27:54 +00:00
stuff = stuff . join ( " \n " ) if stuff . is_a? Array
puts stuff . colorize
2018-02-08 18:11:44 +00:00
end
2020-02-08 17:04:07 +00:00
def self . warn ( stuff = '' )
say ( " {y WARNING: } #{ stuff } " ) if Config . debug?
end
2018-01-28 01:16:09 +00:00
def self . topic_index_width
2018-05-13 04:05:51 +00:00
[ Corpus . topics . size . to_s . length , 2 ] . max
2018-01-28 01:16:09 +00:00
end
2018-01-31 05:25:25 +00:00
def self . topic_author_width
2021-10-24 19:52:03 +00:00
Corpus . authors . map ( & :length ) . max || 1
2018-01-31 05:25:25 +00:00
end
2018-01-28 01:16:09 +00:00
def self . print_index ( index )
2018-01-31 05:25:25 +00:00
# Left-align
2020-03-07 22:59:37 +00:00
'{w ' + ( ( ' ' * topic_index_width ) + index . to_s ) [ ( - topic_index_width ) .. - 1 ] + '}'
2018-01-28 01:16:09 +00:00
end
2018-01-31 05:25:25 +00:00
def self . print_author ( author )
# Right-align
( author . to_s + ( ' ' * topic_author_width ) ) [ 0 .. ( topic_author_width - 1 ) ]
end
2018-05-13 04:05:51 +00:00
def self . topic_header
author_head = ( 'AUTHOR' + ( ' ' * WIDTH ) ) [ 0 .. topic_author_width - 1 ]
2018-05-13 04:45:10 +00:00
'| ' + [ 'ID' , 'U' , 'TIMESTAMP ' , author_head , 'TITLE' ] . join ( ' | ' )
2018-05-13 04:05:51 +00:00
end
2018-01-28 01:16:09 +00:00
end
class Interface
2021-10-24 19:36:56 +00:00
ONE_SHOTS = %w{ compose delete edit freshen help info mark_all_read mark_read next quit reset_display topics unread }
2018-01-28 01:16:09 +00:00
CMD_MAP = {
2021-09-08 03:44:24 +00:00
'?' = > 'help' ,
2021-09-08 01:16:43 +00:00
'c' = > 'compose' ,
2021-09-08 03:44:24 +00:00
'clear' = > 'reset_display' ,
2021-09-08 01:16:43 +00:00
'compose' = > 'compose' ,
'd' = > 'delete' ,
'delete' = > 'delete' ,
2021-09-08 03:44:24 +00:00
'e' = > 'edit' ,
'edit' = > 'edit' ,
2021-09-08 01:16:43 +00:00
'f' = > 'freshen' ,
2021-09-08 03:44:24 +00:00
'freshen' = > 'freshen' ,
'h' = > 'help' ,
'help' = > 'help' ,
2021-09-08 01:16:43 +00:00
'i' = > 'info' ,
'info ' = > 'info' ,
2021-09-08 03:44:24 +00:00
'm' = > 'mark_read' ,
'mark' = > 'mark_read' ,
2021-09-08 01:16:43 +00:00
'mark_all_read' = > 'mark_all_read' ,
2021-09-08 03:44:24 +00:00
'n' = > 'next' ,
'next' = > 'next' ,
'q' = > 'quit' ,
'quit' = > 'quit' ,
'r' = > 'reply' ,
'reply' = > 'reply' ,
'reset' = > 'reset_display' ,
't' = > 'topics' ,
'topics' = > 'topics' ,
'u' = > 'unread' ,
'undelete' = > 'delete' ,
'unread' = > 'unread' ,
2018-01-28 01:16:09 +00:00
}
2018-01-31 05:27:02 +00:00
def reset_display
2018-02-08 18:11:44 +00:00
Display . say ` tput reset ` . chomp
2018-01-31 05:27:02 +00:00
end
2018-04-01 01:10:10 +00:00
def self . info
2020-02-08 16:16:47 +00:00
topic_count = Corpus . topics . size
unread_topic_count = Corpus . unread_topics . size
message_count = Corpus . size
unread_message_count = Corpus . unread_messages . size
2021-10-24 19:52:03 +00:00
author_count = Corpus . authors . size
2020-02-08 16:16:47 +00:00
2018-04-01 01:10:10 +00:00
Display . flowerbox (
" Iris #{ Config :: VERSION } " ,
2020-02-08 16:16:47 +00:00
" #{ topic_count } #{ 'topic' . pluralize ( topic_count ) } , #{ unread_topic_count } unread. " ,
" #{ message_count } #{ 'message' . pluralize ( message_count ) } , #{ unread_message_count } unread. " ,
" #{ author_count } #{ 'author' . pluralize ( author_count ) } . " ,
2018-04-01 01:10:10 +00:00
box_thickness : 0 )
end
def info
Display . say
Interface . info
Display . say
end
2021-09-08 01:16:43 +00:00
def self . mark_all_read
2021-09-08 04:35:01 +00:00
Corpus . mark_as_read ( Corpus . unread_messages . map ( & :hash ) )
2021-09-03 03:19:59 +00:00
end
2021-09-08 01:16:43 +00:00
def mark_all_read
Display . say " Marking all messages as read... "
Interface . mark_all_read
Display . say " Done! "
2021-09-03 03:19:59 +00:00
end
2018-05-11 20:05:43 +00:00
def compose
2021-10-24 19:36:13 +00:00
Display . say 'Writing a new topic.'
message_text = external_editor ( )
if message_text . length < = 1
2021-12-08 20:15:20 +00:00
Display . say '{riv Empty message, discarding...}'
2021-10-24 19:36:13 +00:00
else
Message . new ( message_text ) . save!
Display . say 'Topic saved!'
end
2018-05-11 20:05:43 +00:00
end
2021-09-08 03:44:24 +00:00
def next
Display . say
if Corpus . unread_topics . size == 0
Display . say " {gvi You're all caught up! No new topics to read.} "
return
end
message = Corpus . unread_topics . first
Display . say message . to_topic_display
Display . say
Corpus . mark_as_read ( [ message . hash ] + message . replies . map ( & :hash ) )
end
2021-10-24 19:36:56 +00:00
def reply ( topic_id )
2018-01-31 01:25:56 +00:00
unless topic_id
2021-10-24 19:36:56 +00:00
Display . say " I can't reply to nothing! Include a topic ID to reply to. "
2018-01-31 01:25:56 +00:00
return
end
2018-05-10 22:07:08 +00:00
if parent = ( Corpus . find_topic_by_id ( topic_id ) || Corpus . find_topic_by_hash ( topic_id ) )
2021-10-24 19:36:13 +00:00
reply_topic = parent . hash
2018-01-31 01:25:56 +00:00
else
2018-02-08 18:11:44 +00:00
Display . say " Could not reply; unable to find a topic with ID ' #{ topic_id } ' "
2018-01-31 01:25:56 +00:00
return
end
2020-11-27 00:57:50 +00:00
title = Corpus . find_topic_by_hash ( parent . hash ) . truncated_message ( Display :: TITLE_WIDTH )
2018-02-08 18:11:44 +00:00
Display . say
Display . say " Writing a reply to topic ' #{ title } ' "
2021-10-24 19:36:13 +00:00
message_text = external_editor ( )
if message_text . length < = 1
2021-12-08 20:15:20 +00:00
Display . say '{riv Empty message, discarding...}'
2021-10-24 19:36:13 +00:00
else
Message . new ( message_text , reply_topic ) . save!
Display . say 'Reply saved!'
end
2018-01-31 01:25:56 +00:00
end
2018-05-11 20:05:43 +00:00
def edit ( message_id = nil )
unless message_id
Display . say " I can't edit nothing! Include a message ID to edit. "
return
end
message =
Corpus . find_message_by_hash ( message_id ) ||
Corpus . find_message_by_id ( message_id ) ||
Corpus . find_topic_by_id ( message_id )
unless message
Display . say " Could not edit; unable to find a message with ID ' #{ message_id } ' "
return
end
unless Corpus . is_mine? ( message )
Display . say " Message with ID ' #{ message_id } ' belongs to someone else. "
Display . say " You can only edit your own messages! "
return
end
2020-11-27 00:57:50 +00:00
title = message . truncated_message ( Display :: TITLE_WIDTH )
2018-05-11 20:05:43 +00:00
Display . say
Display . say " Editing message ' #{ title } ' "
2021-10-24 19:36:13 +00:00
message_text = external_editor ( message . message )
if message_text . length < = 1
Display . say 'Empty message, not updating...'
else
Message . edit ( message_text , message )
Display . say 'Message edited!'
end
2018-05-11 20:05:43 +00:00
end
2020-11-27 00:57:50 +00:00
def mark_read ( message_id = nil )
unless message_id
Display . say " I'm not a nihilist; I can't do something with nothing! Include a message ID to mark as read. "
return
end
message =
Corpus . find_message_by_hash ( message_id ) ||
Corpus . find_message_by_id ( message_id ) ||
Corpus . find_topic_by_id ( message_id )
unless message
Display . say " Could not mark as read; unable to find a message with ID ' #{ message_id } ' "
return
end
2021-09-08 03:44:24 +00:00
Corpus . mark_as_read ( [ message . hash ] + message . replies . map ( & :hash ) )
2020-11-27 00:57:50 +00:00
end
2018-05-13 03:34:30 +00:00
def delete ( message_id = nil )
unless message_id
Display . say " I'm not a nihilist; I can't do something with nothing! Include a message ID to delete or undelete. "
return
end
message =
Corpus . find_message_by_hash ( message_id ) ||
Corpus . find_message_by_id ( message_id ) ||
Corpus . find_topic_by_id ( message_id )
unless message
Display . say " Could not delete or undelete; unable to find a message with ID ' #{ message_id } ' "
return
end
unless Corpus . is_mine? ( message )
Display . say " Message with ID ' #{ message_id } ' belongs to someone else. "
Display . say " You can only delete or undelete your own messages! "
return
end
message . delete
2020-11-27 00:57:50 +00:00
title = message . truncated_message ( Display :: TITLE_WIDTH )
2018-05-13 03:34:30 +00:00
Display . say
if message . is_deleted
Display . say " {r Deleted message ' #{ title } ' } "
else
Display . say " {y Undeleted message ' #{ title } ' } "
end
end
2021-10-24 19:36:13 +00:00
def external_editor ( preload_text = nil )
tf = Tempfile . new ( 'iris' )
2018-01-28 01:16:09 +00:00
2021-10-24 19:36:13 +00:00
if preload_text
tf . write ( preload_text )
tf . flush
2018-05-11 20:05:43 +00:00
end
2021-12-09 01:34:35 +00:00
raise " No `$EDITOR` environment variable set! " unless Config :: ENV_EDITOR
2018-05-11 20:05:43 +00:00
2021-12-09 01:34:35 +00:00
system ( " #{ Config :: ENV_EDITOR } #{ tf . path } " )
2021-10-24 19:36:13 +00:00
tf . rewind
message_text = tf . read
tf . unlink
2018-01-28 01:16:09 +00:00
2021-10-24 19:36:13 +00:00
message_text
2018-01-28 01:16:09 +00:00
end
def handle ( line )
2021-10-24 19:36:13 +00:00
tokens = line . split ( / \ s / )
cmd = tokens . first
cmd = CMD_MAP [ cmd ] || cmd
return self . send ( cmd . to_sym ) if ONE_SHOTS . include? ( cmd ) && tokens . length == 1
return show_topic ( cmd ) if cmd =~ / ^ \ d+$ /
# If we've gotten this far, we must have args. Let's handle 'em.
arg = tokens . last
2021-10-24 20:39:30 +00:00
return reply ( arg ) if cmd == 'reply'
return edit ( arg ) if cmd == 'edit'
return delete ( arg ) if cmd == 'delete'
2021-10-24 19:36:13 +00:00
return mark_read ( arg ) if cmd == 'mark_read'
Display . say 'Unrecognized command. Type "help" for a list of available commands.'
2018-01-28 01:16:09 +00:00
end
def show_topic ( num )
index = num . to_i - 1
2020-11-27 00:57:50 +00:00
# TODO: Paginate here
2018-01-28 01:16:09 +00:00
if index > = 0 && index < Corpus . topics . length
2018-01-31 01:25:56 +00:00
msg = Corpus . topics [ index ]
2018-02-08 18:11:44 +00:00
Display . say msg . to_topic_display
Display . say
2021-09-08 03:44:24 +00:00
Corpus . mark_as_read ( [ msg . hash ] + msg . replies . map ( & :hash ) )
2018-01-28 01:16:09 +00:00
else
2018-02-08 18:11:44 +00:00
Display . say 'Could not find a topic with that ID'
2018-01-28 01:16:09 +00:00
end
end
def quit
exit ( 0 )
end
2018-01-31 01:20:06 +00:00
def self . start ( args )
self . new ( args )
2018-01-28 01:16:09 +00:00
end
def prompt
2022-12-21 04:13:32 +00:00
" #{ Config . author } ~> "
2018-01-28 01:16:09 +00:00
end
2018-01-31 01:20:06 +00:00
def initialize ( args )
2018-01-28 01:16:09 +00:00
@history_loaded = false
2018-02-08 18:11:44 +00:00
Display . say " Welcome to Iris v #{ Config :: VERSION } . Type 'help' for a list of commands; Ctrl-D or 'quit' to leave. "
2020-11-27 00:57:50 +00:00
unread
2018-05-10 21:57:47 +00:00
2018-01-28 01:16:09 +00:00
while line = readline ( prompt ) do
2018-02-08 18:11:44 +00:00
handle ( line )
2018-01-28 01:16:09 +00:00
end
end
2020-11-27 00:57:50 +00:00
def unread
Display . say
if Corpus . unread_topics . size == 0
Display . say " {gvi You're all caught up! No new topics to read.} "
return
end
Display . say Display . topic_header
# TODO: Paginate here
Corpus . topics . each_with_index do | topic , index |
if Corpus . unread_topics . include? ( topic )
Display . say topic . to_topic_line ( index + 1 )
end
end
Display . say
end
2018-01-28 01:16:09 +00:00
def topics
2018-02-08 18:11:44 +00:00
Display . say
2018-05-13 04:05:51 +00:00
Display . say Display . topic_header
2020-11-27 00:57:50 +00:00
# TODO: Paginate here
2018-02-08 18:11:44 +00:00
Corpus . topics . each_with_index do | topic , index |
Display . say topic . to_topic_line ( index + 1 )
end
Display . say
2018-01-28 01:16:09 +00:00
end
def help
2018-02-08 18:11:44 +00:00
Display . flowerbox (
" Iris v #{ Config :: VERSION } readline interface " ,
'' ,
'Commands' ,
'========' ,
2018-05-13 03:35:15 +00:00
'READING' ,
2020-11-27 00:57:50 +00:00
'topics, t - List all topics' ,
'unread, u - List all topics with unread messages' ,
'# (topic id) - Read specified topic' ,
2021-09-08 03:44:24 +00:00
'next, n - Read the next unread topic' ,
2020-11-27 00:57:50 +00:00
'mark_read #, m # - Mark the associated topic as read' ,
2021-09-08 04:04:13 +00:00
'mark_all_read - Mark all messages as read' ,
2020-11-27 00:57:50 +00:00
'help, h, ? - Display this text' ,
2018-05-13 03:35:15 +00:00
'' ,
'WRITING' ,
2020-11-27 00:57:50 +00:00
'compose, c - Add a new topic' ,
'reply #, r # - Reply to a specific topic' ,
'edit #, e # - Edit a topic or message' ,
2018-05-13 04:57:42 +00:00
'delete #, d #, undelete # - Delete {u or undelete} a topic or message' ,
2018-05-13 03:35:15 +00:00
'' ,
'SCREEN AND FILE UTILITIES' ,
2020-11-27 00:57:50 +00:00
'freshen, f - Reload to get any new messages' ,
'reset, clear - Fix screen in case of text corruption' ,
'info, i - Display Iris version and message stats' ,
2021-02-15 21:37:28 +00:00
'quit, q - Quit Iris' ,
2018-05-10 21:58:38 +00:00
'' ,
'Full documentation available here:' ,
'https://github.com/Calamitous/iris/blob/master/README.md' ,
2018-02-08 18:11:44 +00:00
box_character : '' )
2018-01-28 01:16:09 +00:00
end
def freshen
Corpus . load
2018-02-08 18:11:44 +00:00
Display . say 'Reloaded!'
2020-12-01 15:43:07 +00:00
unread
2018-01-28 01:16:09 +00:00
end
def readline ( prompt )
if ! @history_loaded && File . exist? ( Config :: HISTORY_FILE )
@history_loaded = true
if File . readable? ( Config :: HISTORY_FILE )
2018-05-31 16:17:32 +00:00
File . readlines ( Config :: HISTORY_FILE ) . each { | l | Readline :: HISTORY . push ( l . chomp ) }
2018-01-28 01:16:09 +00:00
end
end
if line = Readline . readline ( prompt , true )
if File . writable? ( Config :: HISTORY_FILE )
2018-05-31 16:17:32 +00:00
File . open ( Config :: HISTORY_FILE ) { | f | f . write ( line + " \n " ) }
2018-01-28 01:16:09 +00:00
end
return line
else
return nil
end
end
2018-01-31 01:20:06 +00:00
end
class CLI
2018-02-08 18:11:44 +00:00
def self . print_help
Display . flowerbox (
" Iris v #{ Config :: VERSION } command-line " ,
'' ,
'Usage' ,
'========' ,
2018-05-31 16:17:32 +00:00
" #{ Config :: IRIS_SCRIPT } [options] " ,
2018-02-08 18:11:44 +00:00
'' ,
'Options' ,
'========' ,
'--help, -h - Display this text.' ,
'--version, -v - Display the current version of Iris.' ,
2018-04-01 01:10:10 +00:00
'--stats, -s - Display Iris version and message stats.' ,
2018-02-08 18:11:44 +00:00
'--interactive, -i - Enter interactive mode (default)' ,
2021-09-08 04:04:13 +00:00
'--mark-all-read - Mark all messages as read.' ,
2018-03-10 06:39:27 +00:00
'--dump, -d - Dump entire message corpus out.' ,
2020-02-08 16:16:47 +00:00
'--test-file <filename>, -f <filename> - Use the specified test file for messages.' ,
2020-02-08 17:04:07 +00:00
'--debug - Print warnings and debug informtation during use.' ,
2018-02-08 18:11:44 +00:00
'' ,
'If no options are provided, Iris will enter interactive mode.' ,
box_character : '' )
2018-02-01 07:27:51 +00:00
end
2018-01-31 01:20:06 +00:00
def self . start ( args )
2018-02-01 07:27:51 +00:00
if ( args & %w{ -v --version } ) . any?
2018-02-08 18:11:44 +00:00
Display . say " Iris #{ Config :: VERSION } "
2018-02-01 07:27:51 +00:00
exit ( 0 )
end
if ( args & %w{ -h --help } ) . any?
2018-02-08 18:11:44 +00:00
print_help
2018-02-01 07:27:51 +00:00
exit ( 0 )
end
if ( args & %w{ -s --stats } ) . any?
2018-04-01 01:10:10 +00:00
Interface . info
2018-02-01 07:27:51 +00:00
exit ( 0 )
end
2018-03-10 06:39:27 +00:00
if ( args & %w{ -d --dump } ) . any?
puts Corpus . to_json
exit ( 0 )
end
2021-09-08 01:16:43 +00:00
if ( args & %w{ --mark-all-read } ) . any?
Interface . mark_all_read
2021-09-03 03:19:59 +00:00
exit ( 0 )
end
2018-02-08 18:11:44 +00:00
Display . say " Unrecognized option(s) #{ args . join ( ', ' ) } "
Display . say " Try -h for help "
2021-09-08 01:16:43 +00:00
exit ( 1 )
2018-01-31 01:20:06 +00:00
end
end
2018-01-28 01:16:09 +00:00
2018-01-31 01:20:06 +00:00
class Startupper
2021-10-24 20:18:07 +00:00
INTERACTIVE_OPTIONS = %w[ -i --interactive ]
NONINTERACTIVE_OPTIONS = %w[ -d --dump -h --help -v --version -s --stats --mark-all-read ]
NONFILE_OPTIONS = %w[ -h --help -v --version ]
2018-01-31 01:20:06 +00:00
def initialize ( args )
2021-10-26 14:44:29 +00:00
Startupper . perform_file_checks unless NONFILE_OPTIONS . include? ( args )
2018-01-31 01:20:06 +00:00
2020-02-08 16:16:47 +00:00
load_corpus ( args )
2021-10-24 20:18:07 +00:00
is_interactive = ( args & NONINTERACTIVE_OPTIONS ) . none? || ( args & INTERACTIVE_OPTIONS ) . any?
2020-02-08 16:16:47 +00:00
2020-02-08 17:04:07 +00:00
Config . enable_debug_mode if ( args & %w{ --debug } ) . any?
2020-02-08 16:16:47 +00:00
if is_interactive
2018-01-31 01:20:06 +00:00
Interface . start ( args )
else
CLI . start ( args )
end
end
2021-10-26 14:44:29 +00:00
def self . perform_file_checks
2018-01-31 03:27:40 +00:00
unless File . exists? ( Config :: MESSAGE_FILE )
2018-02-08 18:11:44 +00:00
Display . say " You don't have a message file at #{ Config :: MESSAGE_FILE } . "
2018-01-31 03:27:40 +00:00
response = Readline . readline 'Would you like me to create it for you? (y/n) ' , true
if / [Yy] / =~ response
IrisFile . create_message_file
else
2018-02-08 18:11:44 +00:00
Display . say 'Cannot run Iris without a message file!'
2018-01-31 03:27:40 +00:00
exit ( 1 )
end
end
2020-02-08 16:16:47 +00:00
IrisFile . create_read_file
2018-02-07 03:21:44 +00:00
2018-01-31 01:20:06 +00:00
if File . stat ( Config :: MESSAGE_FILE ) . mode != 33188
2018-05-31 16:17:32 +00:00
Display . permissions_error ( Config :: MESSAGE_FILE , 'message' , '-rw-r--r--' , '644' , " Leaving your file with incorrect permissions could allow unauthorized edits! " )
2018-01-31 01:20:06 +00:00
end
2020-02-08 16:16:47 +00:00
if File . stat ( Config . readfile_filename ) . mode != 33188
Display . permissions_error ( Config . readfile_filename , 'read' , '-rw-r--r--' , '644' )
2018-02-07 03:21:44 +00:00
end
2018-02-08 18:11:44 +00:00
if File . stat ( Config :: IRIS_SCRIPT ) . mode != 33261
2018-05-31 16:17:32 +00:00
Display . permissions_error ( Config :: IRIS_SCRIPT , 'Iris' , '-rwxr-xr-x' , '755' , 'If this file has the wrong permissions the program may be tampered with!' )
2018-01-31 01:20:06 +00:00
end
end
2020-02-08 16:16:47 +00:00
def load_corpus ( args )
if ( args & %w{ -f --test-file } ) . any?
filename_idx = ( args . index ( '-f' ) || args . index ( '--test-file' ) ) + 1
filename = args [ filename_idx ]
unless filename
Display . say " Option `--test-file` (`-f`) expects a filename "
exit ( 1 )
end
unless File . exist? ( filename )
Display . say " Could not load test file: #{ filename } "
exit ( 1 )
end
Display . say " Using Iris with test file: #{ filename } "
$test_corpus_file = filename
end
Corpus . load
end
2018-01-28 01:16:09 +00:00
end
2018-01-26 05:59:23 +00:00
2018-02-02 05:59:57 +00:00
Startupper . new ( ARGV ) if __FILE__ == $0
2018-03-30 22:27:54 +00:00