Compare commits

...

6 Commits

5 changed files with 119 additions and 19 deletions

View File

@ -1,5 +1,12 @@
# Changelog
## 1.1.1
* Make discarded message response more prominent
* Try nano if /usr/bin/vim is not set
* Expand the technical documentation
* Flesh out some tests
* Clean up TODO file
## 1.1.0
* Iris now composes messages with $EDITOR instead of using an internal editor
* Remove (broken) feature that automatically selects a reply when not provided with a topic ID

View File

@ -36,7 +36,7 @@ Iris has a readline interface that can be used to navigate the message corpus.
```bash
%> iris
Welcome to Iris v. 1.1.0. Type "help" for a list of commands.; Ctrl-D or 'quit' to leave.
Welcome to Iris v. 1.1.1. Type "help" for a list of commands.; Ctrl-D or 'quit' to leave.
| ID | U | TIMESTAMP | AUTHOR | TITLE
| 1 | | 2018-01-24T05:49:53Z | jimmy_foo@ctrl-c.club | Welcome!
@ -368,7 +368,7 @@ This outputs the current version of Iris, along with messsage, topic, and author
```bash
jennie_minnie@ctrl-c.club~> info
Iris 1.1.0
Iris 1.1.1
22 topics, 0 unread.
50 messages, 0 unread.
10 authors.
@ -402,7 +402,7 @@ iris --version
```
```bash
Iris 1.1.0
Iris 1.1.1
```
---
@ -418,7 +418,7 @@ iris --stats
```
```bash
Iris 1.1.0
Iris 1.1.1
22 topics, 0 unread.
50 messages, 0 unread.
10 authors.
@ -657,7 +657,7 @@ In order to operate correctly and safely, this file _must_ be:
* World-readable
* Owner-writable
* Non-executable
* Owned by the user account that will be storing messages for
* Owned by the user account that it will be storing messages for
```bash
%> ls -la ~/.iris.messages
@ -665,7 +665,71 @@ In order to operate correctly and safely, this file _must_ be:
```
### Messages
Messages fall into one of two categories: topics and replies. Topics are top-level messages. Replies are messages that are attached to a topic.
The message structure is as follows:
```
{
"hash": str,
"edit_hash": str,
"is_deleted": bool,
"data": {
"author": str,
"parent": str,
"timestamp": str,
"message": str
}
}
```
Each field is as follows:
* author: The username of the user who created the message, with the server hostname attached with an @ symbol (ie. `jerry_berry@ctrl-c.club`).
* message: The text of the message. The first line is the title of the message.
* hash: Each message is SHA1 hashed for verification and uniqueness. The author, parent hash, timestamp, and message values go into the hash. (see [Message Hash](#message-hash) for details)
* parent: If the message is a reply, this holds the hash of the topic it's associated with.
* timestamp: The GMT timestamp of when the message was created.
* edit_hash: When a message is edited, a new message is created-- this field holds the hash of the modified message. The client follows the chain of edit hashes to end up at the final, edited message to display. This lets us keep an "undo" history (not yet implemented) and is a marker so the client can display a marker that the message has been edited.
* is_deleted: This is a boolean field that marks whether a message has been deleted. The message is retained so that the structure of topics and replies can be maintained.
* errors (not saved to the file): This is where any issues are held for display. Examples of errors are unparseable usernames or invalid hashes.
#### Message Hash
Each message is SHA1 hashed for verification and uniqueness.
The hash is created by putting `author`, `parent`, `timestamp`, and `message` go into a JSON-formatted string. This string should include no extra whitespace.
For example:
The following message:
author: `jerry_berry@ctrl-c.club`
parent: null
timestamp: `2021-11-25T06:35:34Z`
message: `Howdy!`
Would be turned into the following JSON string, in this order:
```
{"author":"jerry_berry@ctrl-c.club","parent":null,"timestamp":"2021-11-25T06:35:34Z","message":"Howdy!"}
```
This string would then be hashed using SHA1:
```
\xBD\xFD[D\xA0\xF0\xBFw`\x14\xF8)\xCA\xC9n\xFA-\x82\xB9\xBC
```
This hash is then base64 encoded:
```
vf1bRKDwv3dgFPgpyslu+i2Cubw=\n
```
This is the "key" that is used to uniquely identify this version of the message.
##### Bad Hashes
#### Edit Chain
#### Deleted Messages

View File

@ -10,16 +10,12 @@
* Flesh out technical sections
### Bugs
* Replying implicitly to 24 replied to 6 instead-- remove implicit reply?
* Is `Time.now.utc.iso8601` working as expected?
* Fix bug when people are posting from different time zones
* Fix message ordering when editing/deleting multiple messages
* Gracefully handle attempt to "r 1 message"
### Features
* Add "unread" marker to topic replies
* Allow shelling out to editor for message editing
* https://github.com/Calamitous/iris/issues/2
* Add pagination/less for long message lists
* https://github.com/Calamitous/iris/issues/1
* Add local timezone rendering

24
iris.rb
View File

@ -8,8 +8,21 @@ require 'tempfile'
require 'time'
# require 'pry' # Only needed for debugging
class NilClass
def presence
self
end
end
class String
def presence
return nil if self.length == 0
self
end
end
class Config
VERSION = '1.1.0'
VERSION = '1.1.1'
MESSAGE_FILE = "#{ENV['HOME']}/.iris.messages"
HISTORY_FILE = "#{ENV['HOME']}/.iris.history"
IRIS_SCRIPT = __FILE__
@ -17,6 +30,7 @@ class Config
USER = ENV['USER'] || ENV['LOGNAME'] || ENV['USERNAME']
HOSTNAME = `hostname -d`.chomp
AUTHOR = "#{USER}@#{HOSTNAME}"
ENV_EDITOR = ENV['EDITOR'].presence || `which nano`.chomp.presence
@@debug_mode = false
@ -681,7 +695,7 @@ class Interface
message_text = external_editor()
if message_text.length <= 1
Display.say 'Empty message, discarding...'
Display.say '{riv Empty message, discarding...}'
else
Message.new(message_text).save!
Display.say 'Topic saved!'
@ -724,7 +738,7 @@ class Interface
message_text = external_editor()
if message_text.length <= 1
Display.say 'Empty message, discarding...'
Display.say '{riv Empty message, discarding...}'
else
Message.new(message_text, reply_topic).save!
Display.say 'Reply saved!'
@ -827,9 +841,9 @@ class Interface
tf.flush
end
raise "No `$EDITOR` environment variable set!" unless ENV['EDITOR'] && ENV['EDITOR'].length > 0
raise "No `$EDITOR` environment variable set!" unless Config::ENV_EDITOR
system("#{ENV['EDITOR']} #{tf.path}")
system("#{Config::ENV_EDITOR} #{tf.path}")
tf.rewind
message_text = tf.read
tf.unlink

View File

@ -4,6 +4,8 @@ require 'mocha/minitest'
# This allows the test to pretend that user "jerryberry" is logged in.
ENV['USER'] = 'jerryberry'
ENV['EDITOR'] = 'foo/bar'
# Set this before loading the code so that the Config constants load correctly.
$test_corpus_file = "./tests/iris.messages.json"
@ -34,6 +36,10 @@ describe Config do
_(Config::AUTHOR).must_equal "#{Config::USER}@#{Config::HOSTNAME}"
end
it 'has the $EDITOR environment variable' do
_(Config::ENV_EDITOR).must_equal 'foo/bar'
end
describe '.find_files' do
it 'looks up all the Iris message files on the system' do
# I am so sorry about this `expects` clause
@ -97,10 +103,13 @@ describe Corpus do
end
it 'returns an empty array if the hash is not a parent of any other messages' do
_(Corpus.find_all_by_parent_hash(nil)).must_equal []
end
it 'returns an empty array if the hash is not found in the corpus' do
_(Corpus.find_all_by_parent_hash('GoofMcDoof')).must_equal []
end
it 'returns an empty array if the hash is not found in the corpus'
it 'returns the messages associated with the parent hash'
end
@ -110,8 +119,13 @@ describe Corpus do
end
describe 'when an index string is passed in' do
it 'returns nil if the topic is not found'
it 'returns the associated topic'
it 'returns nil if the topic is not found' do
assert_nil Corpus.find_topic_by_id('InvalidTopicId')
end
it 'returns the associated topic' do
_(Corpus.find_topic_by_id(1).message).must_equal 'Test'
end
end
end
@ -121,8 +135,13 @@ describe Corpus do
end
describe 'when a hash string is passed in' do
it 'returns nil if the topic is not found'
it 'returns the associated topic'
it 'returns nil if the topic is not found' do
assert_nil Corpus.find_topic_by_hash('BadHash')
end
it 'returns the associated topic' do
_(Corpus.find_topic_by_hash("gpY2WW/jGcH+BODgySCwDANJlIM=\n").message).must_equal 'Test'
end
end
end
end