elephrame 0.2.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +54 -6
- data/examples/README.md +5 -0
- data/examples/interact.rb +6 -2
- data/examples/reply.rb +1 -2
- data/lib/elephrame/bot.rb +60 -5
- data/lib/elephrame/mix/bots.rb +20 -2
- data/lib/elephrame/rest/bots.rb +13 -0
- data/lib/elephrame/rest/rest.rb +8 -1
- data/lib/elephrame/streaming/bots.rb +10 -1
- data/lib/elephrame/streaming/streaming.rb +66 -2
- data/lib/elephrame/util/status.rb +16 -0
- data/lib/elephrame/version.rb +1 -1
- data/lib/elephrame.rb +4 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9bacbf531fa6368eb2e401e3492192eb9ae230966edc62f3971f3566430b64f5
|
4
|
+
data.tar.gz: 8a09ce7796b7acd043f73195fd7511eda01cd8f5abf6de2aa081f17aec1fa46c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f836c9f830ea5a24a52f8e708b96a6e26ecfff5c03182b0ccbf98787442f9ab4b9508bdd2ff9a340cd318723d265f97bd07506725048d2b2313bd86cd5451e99
|
7
|
+
data.tar.gz: 356c31e481d1d7892759fed57dbc41610364624e60725c23ff53f788a2245e66e5a6843f88ab4cf7295065dcfcf193a8f252167f57f9c89195482714543a548d
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
# Elephrame
|
2
2
|
|
3
|
-
|
3
|
+
[![Gem Version](https://badge.fury.io/rb/elephrame.svg)](https://badge.fury.io/rb/elephrame)
|
4
|
+
[RubyDoc](https://www.rubydoc.info/github/theZacAttacks/elephrame/)
|
4
5
|
|
5
|
-
|
6
|
+
Elephant-Framework
|
7
|
+
|
8
|
+
A framework that helps simplify the creation of bots for mastodon/pleroma
|
9
|
+
|
10
|
+
Uses [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler) in the backend
|
6
11
|
|
7
12
|
## Installation
|
8
13
|
|
@@ -20,20 +25,63 @@ Or install it yourself as:
|
|
20
25
|
|
21
26
|
$ gem install elephrame
|
22
27
|
|
28
|
+
## Quickstart
|
29
|
+
|
30
|
+
bot that posts "i'm gay" every three hours:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require 'elephrame'
|
34
|
+
|
35
|
+
my_bot = Elephrame::Bots::Periodic.new '3h'
|
36
|
+
|
37
|
+
my_bot.run { |bot|
|
38
|
+
bot.post("i'm gay")
|
39
|
+
}
|
40
|
+
```
|
41
|
+
|
42
|
+
$ INSTANCE="https://mastodon.social" TOKEN="your_access_token" ruby bot.rb
|
43
|
+
|
44
|
+
Check the [examples](https://github.com/theZacAttacks/elephrame/tree/master/examples) directory for more example bots
|
45
|
+
|
46
|
+
### Bot Types
|
47
|
+
|
48
|
+
So far the framework support 4 bot types: Periodic, Interact, PeroidInteract, Reply
|
49
|
+
|
50
|
+
- `Periodic` supports posting on a set schedule
|
51
|
+
- `Interact` supports callbacks for each type of interaction (favorites, boosts, replies, follows)
|
52
|
+
- `PeriodInteract` supports both of the above (I know, this isn't a good name)
|
53
|
+
- `Reply` only supports replying to mentions
|
54
|
+
|
55
|
+
The string passed to `Periodic` and `PeroidInteract` must be either a 'Duration' string or a 'Cron' string, as parsable by [fugit](https://github.com/floraison/fugit)
|
56
|
+
|
23
57
|
## Usage
|
24
58
|
|
25
|
-
|
59
|
+
All the documentation is over at [RubyDoc](https://www.rubydoc.info/github/theZacAttacks/elephrame/)!
|
60
|
+
|
61
|
+
Every place that accepts a block provides access to the bot object. This allows for easy access to some provided helper methods, as well as the actual mastodon access object.
|
62
|
+
|
63
|
+
Exposed methods from bot object:
|
64
|
+
|
65
|
+
- `client` this is the underlying mastodon rest client we use in the backend. use this to make custom calls to the api for which we don't provide a helper
|
66
|
+
- `username` the name of the bot as fetched by verify_credentials
|
67
|
+
- `strip_html` (defaults to true) if set, the framework will automatically strip all html symbols from the post content
|
68
|
+
- `post(content, visibility: 'unlisted', spoiler: '', reply_id: '', media: [])` this provides an easy way to post statuses from inside code blocks
|
69
|
+
- `reply(content)` a shorthand method to reply to the last mention (Note: doesn't automatically @ the other user/s)
|
70
|
+
- `find_ancestor(id, depth = 10, stop_at = 1)` looks backwards through reply chains for the most recent post the bot made starting at post `id` until it hits `depth` number of posts, or finds `stop_at` number of it's own posts
|
71
|
+
- `no_bot?(account_id)` returns true if user with `account_id` has some form of "#NoBot" in their bio
|
72
|
+
|
73
|
+
(See RubyDocs for source code documentation)
|
26
74
|
|
27
75
|
## Development
|
28
76
|
|
29
77
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
30
78
|
|
31
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
79
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
32
80
|
|
33
81
|
## Contributing
|
34
82
|
|
35
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
83
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/theZacAttacks/elephrame. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
36
84
|
|
37
85
|
## Code of Conduct
|
38
86
|
|
39
|
-
Everyone interacting in the Elephrame project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/
|
87
|
+
Everyone interacting in the Elephrame project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/theZacAttacks/elephrame/blob/master/CODE_OF_CONDUCT.md).
|
data/examples/README.md
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
# Elephrame Examples
|
2
|
+
|
3
|
+
Follow the gem installation from the [main README](https://github.com/theZacAttacks/elephrame/tree/master/README.md) and then do the following, where `[file]` is one of the example ruby scripts
|
4
|
+
|
5
|
+
$ INSTANCE="https://mastodon.social" TOKEN="your_token_here" ruby [file]
|
data/examples/interact.rb
CHANGED
@@ -3,8 +3,12 @@ require 'elephrame'
|
|
3
3
|
interacter = Elephrame::Bots::Interact.new
|
4
4
|
|
5
5
|
interacter.on_reply { |bot, post|
|
6
|
-
|
7
|
-
|
6
|
+
|
7
|
+
#bot.post("@#{post.account.acct} Thanks for helping me test stuff :3",
|
8
|
+
# reply_id: post.id, visibility: post.visibility)
|
9
|
+
|
10
|
+
## this can be simplified to one line
|
11
|
+
bot.reply("@#{post.account.acct} Thanks for helping me test stuff :3")
|
8
12
|
}
|
9
13
|
|
10
14
|
interacter.on_fave { |bot, notif|
|
data/examples/reply.rb
CHANGED
data/lib/elephrame/bot.rb
CHANGED
@@ -1,19 +1,39 @@
|
|
1
1
|
module Elephrame
|
2
2
|
module Bots
|
3
|
-
|
3
|
+
|
4
|
+
##
|
4
5
|
# a superclass for other bots
|
5
|
-
# holds common functions and
|
6
|
+
# holds common functions and variables
|
7
|
+
|
6
8
|
class BaseBot
|
7
|
-
attr_reader :client
|
9
|
+
attr_reader :client, :username
|
10
|
+
attr_accessor :strip_html
|
8
11
|
|
12
|
+
##
|
13
|
+
# Sets up our REST client, gets and saves our username, sets default
|
14
|
+
# value for strip_html (true)
|
15
|
+
#
|
16
|
+
# @return [Elephrame::Bots::BaseBot]
|
17
|
+
|
9
18
|
def initialize
|
10
19
|
@client = Mastodon::REST::Client.new(base_url: ENV['INSTANCE'],
|
11
20
|
bearer_token: ENV['TOKEN'])
|
21
|
+
@username = @client.verify_credentials().acct
|
22
|
+
@strip_html = true
|
12
23
|
end
|
24
|
+
|
25
|
+
##
|
26
|
+
# Creates a post, uploading media if need be
|
27
|
+
#
|
28
|
+
# @param text [String] text to post
|
29
|
+
# @param visibility [String] visibility level
|
30
|
+
# @param spoiler [String] text to use as content warning
|
31
|
+
# @param reply_id [String] id of post to reply to
|
32
|
+
# @param media [Array<String>] array of file paths
|
13
33
|
|
14
34
|
def post(text, visibility: 'unlisted', spoiler: '', reply_id: '', media: [])
|
15
35
|
|
16
|
-
|
36
|
+
unless media.empty?
|
17
37
|
media.collect! {|m|
|
18
38
|
@client.upload_media(m).id
|
19
39
|
}
|
@@ -28,8 +48,43 @@ module Elephrame
|
|
28
48
|
|
29
49
|
@client.create_status text, options
|
30
50
|
end
|
31
|
-
end
|
32
51
|
|
52
|
+
|
53
|
+
##
|
54
|
+
# Finds most recent post by bot in the ancestors of a provided post
|
55
|
+
#
|
56
|
+
# @param id [String] post to base search off of
|
57
|
+
# @param depth [Integer] max number of posts to search
|
58
|
+
# @param stop_at [Integer] defines which ancestor to stop at
|
59
|
+
#
|
60
|
+
# @return [Mastodon::Status]
|
61
|
+
|
62
|
+
def find_ancestor(id, depth = 10, stop_at = 1)
|
63
|
+
depth.each {
|
64
|
+
post = @client.status(id) unless id.nil?
|
65
|
+
id = post.id
|
66
|
+
|
67
|
+
stop_at -= 1 if post.account.acct == @username
|
68
|
+
|
69
|
+
return post if stop_at.zero?
|
70
|
+
}
|
71
|
+
|
72
|
+
return nil
|
73
|
+
end
|
74
|
+
|
75
|
+
##
|
76
|
+
# Checks to see if a user has some form of "#NoBot" in their bio
|
77
|
+
# (so we can make make friendly bots easier!)
|
78
|
+
#
|
79
|
+
# @param account_id [String] id of account to check bio
|
80
|
+
#
|
81
|
+
# @return [Bool]
|
82
|
+
|
83
|
+
def no_bot? account_id
|
84
|
+
@client.account(account_id).note =~ /#?NoBot/i
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
33
88
|
end
|
34
89
|
end
|
35
90
|
|
data/lib/elephrame/mix/bots.rb
CHANGED
@@ -4,11 +4,24 @@ require_relative '../bot'
|
|
4
4
|
|
5
5
|
module Elephrame
|
6
6
|
module Bots
|
7
|
-
|
8
|
-
|
7
|
+
|
8
|
+
##
|
9
|
+
# a bot that posts things on an interval but can also respond
|
10
|
+
# to interactions
|
11
|
+
#
|
12
|
+
# requires on_* variables to be set before running, otherwise the bot
|
13
|
+
# won't react to interactions
|
14
|
+
|
9
15
|
class PeriodInteract < BaseBot
|
10
16
|
include Elephrame::Scheduler
|
11
17
|
include Elephrame::AllInteractions
|
18
|
+
|
19
|
+
##
|
20
|
+
# creates a new PeriodInteract bot
|
21
|
+
#
|
22
|
+
# @param intv [String] string specifying interval to post
|
23
|
+
#
|
24
|
+
# @return [Elephrame::Bots::PeriodInteract]
|
12
25
|
|
13
26
|
def initialize intv
|
14
27
|
super()
|
@@ -16,6 +29,11 @@ module Elephrame
|
|
16
29
|
setup_scheduler intv
|
17
30
|
setup_streaming
|
18
31
|
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# Runs the bot. requires a block for periodic post logic, but relies on
|
35
|
+
# on_* functions for interaction logic. See Elephrame::AllInteractions
|
36
|
+
# for more details.
|
19
37
|
|
20
38
|
def run
|
21
39
|
run_scheduled &Proc.new
|
data/lib/elephrame/rest/bots.rb
CHANGED
@@ -3,10 +3,23 @@ require_relative '../bot'
|
|
3
3
|
|
4
4
|
module Elephrame
|
5
5
|
module Bots
|
6
|
+
|
7
|
+
##
|
6
8
|
# a bot that runs commands based off of
|
7
9
|
# an interval or a cron string
|
10
|
+
|
8
11
|
class Periodic < BaseBot
|
9
12
|
include Elephrame::Scheduler
|
13
|
+
|
14
|
+
##
|
15
|
+
# creates a new Periodic bot
|
16
|
+
#
|
17
|
+
# @param intv [String] string specifying interval to post.
|
18
|
+
# ex: '3h' (every 3 hours) '20m' (every 20 minutes)
|
19
|
+
# '00 12 * * *' (every day at 12:00)
|
20
|
+
# '00 00 25 12 *' (midnight on christmas)
|
21
|
+
#
|
22
|
+
# @return [Elephrame::Bots::Periodic]
|
10
23
|
|
11
24
|
def initialize intv
|
12
25
|
super()
|
data/lib/elephrame/rest/rest.rb
CHANGED
@@ -2,7 +2,11 @@ module Elephrame
|
|
2
2
|
module Scheduler
|
3
3
|
attr :scheduler, :interval
|
4
4
|
attr_reader :schedule
|
5
|
-
|
5
|
+
|
6
|
+
##
|
7
|
+
# Creates a new scheduler
|
8
|
+
#
|
9
|
+
# @param intv [String] string specifying interval to post
|
6
10
|
def setup_scheduler intv
|
7
11
|
require 'rufus-scheduler'
|
8
12
|
|
@@ -10,6 +14,9 @@ module Elephrame
|
|
10
14
|
@interval = intv
|
11
15
|
@scheduler = Rufus::Scheduler.new
|
12
16
|
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Runs the schedule. Requires a block to be passed to it.
|
13
20
|
|
14
21
|
def run_scheduled
|
15
22
|
@scheduler.repeat @interval do |j|
|
@@ -4,7 +4,12 @@ require_relative '../bot'
|
|
4
4
|
module Elephrame
|
5
5
|
module Bots
|
6
6
|
|
7
|
+
##
|
7
8
|
# a bot that can respond to all interactions
|
9
|
+
#
|
10
|
+
# Call on_fave, on_follow, on_reply, or on_boost with a block
|
11
|
+
# before calling run. Otherwise bot will do nothing.
|
12
|
+
|
8
13
|
class Interact < BaseBot
|
9
14
|
include Elephrame::Streaming
|
10
15
|
include Elephrame::AllInteractions
|
@@ -16,9 +21,13 @@ module Elephrame
|
|
16
21
|
end
|
17
22
|
end
|
18
23
|
|
24
|
+
|
25
|
+
##
|
19
26
|
# a bot that only replies when mentioned
|
27
|
+
#
|
20
28
|
# run accepts a block, but also supports
|
21
|
-
# use of on_reply
|
29
|
+
# use of on_reply (See Elephrame::AllInteractions for more details)
|
30
|
+
|
22
31
|
class Reply < BaseBot
|
23
32
|
include Elephrame::Streaming
|
24
33
|
include Elephrame::Reply
|
@@ -2,23 +2,69 @@ module Elephrame
|
|
2
2
|
module Streaming
|
3
3
|
attr :streamer
|
4
4
|
|
5
|
+
##
|
6
|
+
# Creates the stream client
|
7
|
+
|
5
8
|
def setup_streaming
|
6
9
|
@streamer = Mastodon::Streaming::Client.new(base_url: ENV['INSTANCE'],
|
7
10
|
bearer_token: ENV['TOKEN'])
|
8
11
|
end
|
12
|
+
|
9
13
|
end
|
10
|
-
|
14
|
+
|
15
|
+
|
11
16
|
module Reply
|
12
|
-
attr :on_reply
|
17
|
+
attr :on_reply, :mention_data
|
13
18
|
|
19
|
+
##
|
20
|
+
# Sets on_reply equal to +block+
|
21
|
+
|
14
22
|
def on_reply &block
|
15
23
|
@on_reply = block
|
16
24
|
end
|
17
25
|
|
26
|
+
##
|
27
|
+
# Replies to the last mention the bot recieved using the mention's
|
28
|
+
# visibility and spoiler with +text+
|
29
|
+
#
|
30
|
+
# *DOES NOT AUTOMATICALLY INCLUDE @'S*
|
31
|
+
#
|
32
|
+
# @param text [String] text to post as a reply
|
33
|
+
|
34
|
+
def reply(text)
|
35
|
+
|
36
|
+
# maybe also @ everyone from the mention? idk that seems like a bad idea tbh
|
37
|
+
post(text, @mention_data[:vis], @mention_data[:spoiler],
|
38
|
+
@mention_data[:id])
|
39
|
+
end
|
40
|
+
|
41
|
+
##
|
42
|
+
# Stores select data about a post into a hash for later use
|
43
|
+
#
|
44
|
+
# @param mention [Mastodon::Status] the most recent mention the bot received
|
45
|
+
|
46
|
+
def store_mention_data(mention)
|
47
|
+
@mention_data = {
|
48
|
+
id: mention.id,
|
49
|
+
vis: mention.visibility,
|
50
|
+
spoiler: mention.spoiler_text,
|
51
|
+
mentions: mention.mentions
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
##
|
56
|
+
# Starts a loop that checks for mentions from the authenticated user account
|
57
|
+
# running a supplied block or, if a block is not provided, on_reply
|
58
|
+
|
18
59
|
def run_reply
|
19
60
|
@streamer.user do |update|
|
20
61
|
next unless update.kind_of? Mastodon::Notification and update.type == 'mention'
|
21
62
|
|
63
|
+
# this makes it so .content calls strip instead
|
64
|
+
update.status.class.module_eval { alias_method :content, :strip } if @strip_html
|
65
|
+
|
66
|
+
store_mention_data update.status
|
67
|
+
|
22
68
|
if block_given?
|
23
69
|
yield(self, update.status)
|
24
70
|
else
|
@@ -30,21 +76,35 @@ module Elephrame
|
|
30
76
|
alias_method :run, :run_reply
|
31
77
|
end
|
32
78
|
|
79
|
+
|
33
80
|
module AllInteractions
|
34
81
|
include Elephrame::Reply
|
35
82
|
attr :on_fave, :on_boost, :on_follow
|
83
|
+
|
84
|
+
##
|
85
|
+
# Sets on_fave equal to +block+
|
36
86
|
|
37
87
|
def on_fave &block
|
38
88
|
@on_fave = block
|
39
89
|
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# Sets on_boost to +block+
|
40
93
|
|
41
94
|
def on_boost &block
|
42
95
|
@on_boost = block
|
43
96
|
end
|
97
|
+
|
98
|
+
##
|
99
|
+
# Sets on_follow to +block+
|
44
100
|
|
45
101
|
def on_follow &block
|
46
102
|
@on_follow = block
|
47
103
|
end
|
104
|
+
|
105
|
+
##
|
106
|
+
# Starts a loop that checks for any notifications for the authenticated
|
107
|
+
# user, running the appropriate stored proc when needed
|
48
108
|
|
49
109
|
def run_interact
|
50
110
|
@streamer.user do |update|
|
@@ -53,6 +113,10 @@ module Elephrame
|
|
53
113
|
case update.type
|
54
114
|
|
55
115
|
when 'mention'
|
116
|
+
|
117
|
+
# this makes it so .content calls strip instead
|
118
|
+
update.status.class.module_eval { alias_method :content, :strip } if @strip_html
|
119
|
+
store_mention_data update.status
|
56
120
|
@on_reply.call(self, update.status) unless @on_reply.nil?
|
57
121
|
|
58
122
|
when 'reblog'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Mastodon
|
2
|
+
class Status
|
3
|
+
alias_method :rcontent, :content
|
4
|
+
|
5
|
+
##
|
6
|
+
# Strips all html tags out of +content+
|
7
|
+
#
|
8
|
+
# @return [String]
|
9
|
+
|
10
|
+
def strip
|
11
|
+
rcontent.gsub(/<\/p><p>/, "\n")
|
12
|
+
.gsub(/<("[^"]*"|'[^']*'|[^'">])*>/, '')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
data/lib/elephrame/version.rb
CHANGED
data/lib/elephrame.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require 'moostodon'
|
2
|
-
require_relative
|
2
|
+
require_relative 'elephrame/util/status'
|
3
|
+
|
4
|
+
require_relative 'elephrame/version'
|
3
5
|
require_relative 'elephrame/streaming/bots'
|
4
6
|
require_relative 'elephrame/rest/bots'
|
5
7
|
require_relative 'elephrame/mix/bots'
|
8
|
+
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: elephrame
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zac
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-11-
|
11
|
+
date: 2018-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -98,6 +98,7 @@ files:
|
|
98
98
|
- bin/console
|
99
99
|
- bin/setup
|
100
100
|
- elephrame.gemspec
|
101
|
+
- examples/README.md
|
101
102
|
- examples/combined.rb
|
102
103
|
- examples/interact.rb
|
103
104
|
- examples/periodic.rb
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- lib/elephrame/rest/rest.rb
|
110
111
|
- lib/elephrame/streaming/bots.rb
|
111
112
|
- lib/elephrame/streaming/streaming.rb
|
113
|
+
- lib/elephrame/util/status.rb
|
112
114
|
- lib/elephrame/version.rb
|
113
115
|
homepage: https://github.com/theZacAttacks/elephrame
|
114
116
|
licenses:
|