tumblr_to_dayone 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +1 -0
- data/bin/tumblr_to_dayone +7 -0
- data/lib/tumblr_to_dayone/dayone.rb +32 -0
- data/lib/tumblr_to_dayone/prompt.rb +37 -0
- data/lib/tumblr_to_dayone/tumblr/post.rb +56 -0
- data/lib/tumblr_to_dayone/tumblr.rb +52 -0
- data/lib/tumblr_to_dayone/version.rb +3 -0
- data/lib/tumblr_to_dayone.rb +86 -0
- data/tumblr_to_dayone.gemspec +25 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjJlZGVjYjVjMTFjOGQ0NWVjN2M1ZTUwN2M2ZGIyNzA0NGM2Y2FmYw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
YjdhYWIzYzg5YjIzYmJmMjQ4OGQ5ZWM2ZmQxNzJiY2RkZjNmZTc2Zg==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NzE2MWYwMjdjOTI1NzM2OWYxNDZiZDVhNTlmYjA1OWIzNmQ5OTliZDliNmM2
|
10
|
+
MjE3ZGQzZGUzMjk2MjUzMDBmOTFkMmE2N2UzZGNmNDlmOWUzNjExOGFhMWVk
|
11
|
+
NDRkYTk5NmUyZjFhYmFkY2Y5OTZiZWIwZDhhOGEyNjRiY2FiZTY=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
YjI2MzkzZWUxZjE5Yjk2YWE0NTMyYjEzOWViMjQ1ZWQxNjY5ODdkODc3OTQ1
|
14
|
+
MzNmNzg0ZWFkMzAwMzA2YzAyNjc4YWEzYjE2MDZmYjJiZDA0YjcyODEwNzEy
|
15
|
+
MzNlMzA1NWZhNDczNjk3MWU3MTRiZTUzMjE0M2QwOTYzMjI2NDM=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Joey Meyer
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# Tumblr to Day One
|
2
|
+
|
3
|
+
A simple command prompt that easily allows you to add posts from a [Tumblr](http://tumblr.com) blog to your [Day One](http://dayoneapp.com) journal.
|
4
|
+
|
5
|
+
## Usage
|
6
|
+
|
7
|
+
Get started by installing the gem:
|
8
|
+
|
9
|
+
$ gem install tumblr_to_dayone
|
10
|
+
|
11
|
+
You will also need to install the [Day One CLI](http://dayoneapp.com/tools/).
|
12
|
+
|
13
|
+
After that you are ready to go:
|
14
|
+
|
15
|
+
$ tumblr_to_dayone
|
16
|
+
|
17
|
+
This command starts a prompt and asks a few questions to get things setup:
|
18
|
+
|
19
|
+
* Name of the blog
|
20
|
+
* Password (if there is one)
|
21
|
+
* Automatically add all posts – or choose which ones to add one by one
|
22
|
+
* Path to your Day One journal (leave blank if it's the default path)
|
23
|
+
|
24
|
+
After that it is ready to go and will start pulling down your Tumblr posts to either add one by one or all at once.
|
25
|
+
|
26
|
+
## Creator
|
27
|
+
|
28
|
+
[Joey Meyer](http://joeymeyer.com)
|
29
|
+
|
30
|
+
## License
|
31
|
+
|
32
|
+
tumblr_to_dayone is available under the MIT license. See the LICENSE file for more info.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'shellwords'
|
2
|
+
|
3
|
+
module Dayone
|
4
|
+
|
5
|
+
def self.cli_installed?
|
6
|
+
`which dayone`.length > 0
|
7
|
+
end
|
8
|
+
|
9
|
+
# options:
|
10
|
+
#
|
11
|
+
# date - Creation date of the entry
|
12
|
+
# starred - Whether or not to star the entry
|
13
|
+
# photo_path - File path to a photo to attach to entry
|
14
|
+
# journal_path - Location of Day One Journal file
|
15
|
+
#
|
16
|
+
|
17
|
+
def self.create_post(post, options = {})
|
18
|
+
return false unless Dayone.cli_installed? && post
|
19
|
+
|
20
|
+
arguments = {
|
21
|
+
:d => options[:date],
|
22
|
+
:s => options[:starred],
|
23
|
+
:p => options[:photo_path],
|
24
|
+
:j => options[:journal_path]
|
25
|
+
}.map { |k,v| "-#{k}=\"#{v}\"" unless !v }.compact.join(" ")
|
26
|
+
|
27
|
+
output = `echo #{Shellwords.escape(post)} | dayone #{arguments} new`
|
28
|
+
|
29
|
+
output.start_with?("New entry")
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'tumblr_to_dayone'
|
2
|
+
|
3
|
+
module TumblrToDayone
|
4
|
+
module Prompt
|
5
|
+
|
6
|
+
def self.start
|
7
|
+
if Dayone.cli_installed?
|
8
|
+
puts "What is the name of your tumblr blog? (<name>.tumblr.com)"
|
9
|
+
blog_name = gets.chomp
|
10
|
+
|
11
|
+
puts "What is the password of your tumblr blog? (leave blank if none)"
|
12
|
+
password_input = gets.chomp
|
13
|
+
blog_password = password_input unless password_input.empty?
|
14
|
+
|
15
|
+
puts "Automatically add all blog posts? (y/n)"
|
16
|
+
automatic = gets.chomp == 'y'
|
17
|
+
|
18
|
+
puts "What is the location of your Dayone.journal? (leave blank if it is in the default location)"
|
19
|
+
journal_path_input = gets.chomp
|
20
|
+
journal_path = journal_path_input unless journal_path_input.empty?
|
21
|
+
|
22
|
+
TumblrToDayone.add_tumblr_posts_to_dayone(blog_name, :password => blog_password, :automatically_add_each_post => automatic, :journal_path => journal_path) do |post|
|
23
|
+
puts "- " * 30
|
24
|
+
puts post
|
25
|
+
puts "- " * 30
|
26
|
+
puts "Would you like to add this post to Day One? (y: yes, s: yes and star it, n: no, exit: exit the prompt)"
|
27
|
+
gets.chomp.downcase.to_sym
|
28
|
+
end
|
29
|
+
|
30
|
+
puts "Done."
|
31
|
+
else
|
32
|
+
puts "Download and install Day One CLI to continue. (http://dayoneapp.com/tools/)"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'reverse_markdown'
|
3
|
+
|
4
|
+
module Tumblr
|
5
|
+
class Post
|
6
|
+
attr_accessor :type, :created_at, :title, :body, :photo_url, :video_player, :caption, :tags
|
7
|
+
|
8
|
+
def initialize(post_hash)
|
9
|
+
self.type = post_hash["type"]
|
10
|
+
self.created_at = Time.at(post_hash["unix-timestamp"] || Time.now.to_i)
|
11
|
+
self.title = post_hash["regular-title"]
|
12
|
+
self.body = convert_to_markdown(post_hash["regular-body"]) || link_markdown(post_hash["link-text"], post_hash["link-url"])
|
13
|
+
self.photo_url = largest_photo_url(post_hash)
|
14
|
+
self.video_player = post_hash["video-player"]
|
15
|
+
self.caption = convert_to_markdown(post_hash["photo-caption"] || post_hash["video-caption"])
|
16
|
+
self.tags = post_hash["tags"] || []
|
17
|
+
end
|
18
|
+
|
19
|
+
def full_body
|
20
|
+
[
|
21
|
+
self.title ? "# #{self.title}" : nil,
|
22
|
+
self.video_player ? video_player : nil,
|
23
|
+
self.body ? self.body : nil,
|
24
|
+
self.caption ? self.caption : nil,
|
25
|
+
self.tags && !self.tags.empty? ? self.tags.map {|tag| "\\##{tag}"}.join(" ") : nil
|
26
|
+
].compact.join("\n\n")
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
"Tumblr post created on #{self.created_at}\ntitle: #{self.title ? self.title : "<no title>"}\nphoto: #{!!self.photo_url}\nbody:\n#{self.full_body}\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
PHOTO_URL_KEY_PREFIX = "photo-url-"
|
36
|
+
|
37
|
+
def largest_photo_url(post_hash)
|
38
|
+
largest_width = post_hash.keys.map{ |key| key.match(PHOTO_URL_KEY_PREFIX) ? key.gsub(PHOTO_URL_KEY_PREFIX, "").to_i : nil }.compact.max
|
39
|
+
|
40
|
+
largest_width ? post_hash["#{PHOTO_URL_KEY_PREFIX}#{largest_width}"] : nil
|
41
|
+
end
|
42
|
+
|
43
|
+
def convert_to_markdown(content)
|
44
|
+
return nil unless content
|
45
|
+
|
46
|
+
ReverseMarkdown.parse(CGI.unescapeHTML(content))
|
47
|
+
end
|
48
|
+
|
49
|
+
def link_markdown(link_text, link_url)
|
50
|
+
return nil unless link_text && link_url
|
51
|
+
|
52
|
+
"[#{link_text}](#{link_url})"
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'net/http'
|
3
|
+
require 'tumblr_to_dayone/tumblr/post'
|
4
|
+
|
5
|
+
module Tumblr
|
6
|
+
|
7
|
+
# options:
|
8
|
+
#
|
9
|
+
# start - The post offset to start from. The default is 0.
|
10
|
+
# num - The number of posts to return. The default is 20, and the maximum is 50.
|
11
|
+
# type - The type of posts to return. If unspecified or empty, all types of posts are returned. Must be one of text, quote, photo, link, chat, video, or audio.
|
12
|
+
# id - A specific post ID to return. Use instead of start, num, or type.
|
13
|
+
# filter - Alternate filter to run on the text content. Allowed values:
|
14
|
+
# text: Plain text only. No HTML.
|
15
|
+
# none: No post-processing. Output exactly what the author entered. (Note: Some authors write in Markdown, which will not be converted to HTML when this option is used.)
|
16
|
+
# tagged - Return posts with this tag in reverse-chronological order (newest first). Optionally specify chrono=1 to sort in chronological order (oldest first).
|
17
|
+
# search - Search for posts with this query.
|
18
|
+
#
|
19
|
+
|
20
|
+
def self.posts(title, password = nil, options = {})
|
21
|
+
uri = URI.parse("http://#{title}.tumblr.com/api/read/json")
|
22
|
+
uri.query = URI.encode_www_form(options)
|
23
|
+
|
24
|
+
request = Net::HTTP::Get.new(uri.request_uri)
|
25
|
+
request.basic_auth title, password if password
|
26
|
+
|
27
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
28
|
+
response = http.request(request)
|
29
|
+
|
30
|
+
if response.is_a?(Net::HTTPSuccess)
|
31
|
+
body = JSON.parse(response.body[22..-3])
|
32
|
+
|
33
|
+
total_posts = body["posts-total"].to_i
|
34
|
+
post_hashes = body["posts"]
|
35
|
+
|
36
|
+
posts = post_hashes.map { |post_hash| Tumblr::Post.new(post_hash) }
|
37
|
+
|
38
|
+
if block_given?
|
39
|
+
yield posts, total_posts
|
40
|
+
else
|
41
|
+
posts
|
42
|
+
end
|
43
|
+
else
|
44
|
+
raise TumblrPostsAPIError, "Failed to get posts, server returned #{response.code} #{response.message}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
class TumblrPostsAPIError < StandardError
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require "tumblr_to_dayone/version"
|
2
|
+
require 'tumblr_to_dayone/tumblr'
|
3
|
+
require 'tumblr_to_dayone/dayone'
|
4
|
+
|
5
|
+
module TumblrToDayone
|
6
|
+
|
7
|
+
# options:
|
8
|
+
#
|
9
|
+
# password - Password for blog (if there is one)
|
10
|
+
# filter - Only get Tumblr posts based on type (text, quote, photo, link, chat, video, or audio)
|
11
|
+
# automatically_add_each_post - Automatically add each Tumblr post or ask before adding each one (defaults to false)
|
12
|
+
# journal_path - Location of Day One Journal file
|
13
|
+
#
|
14
|
+
|
15
|
+
def self.add_tumblr_posts_to_dayone(blog, options={})
|
16
|
+
begin
|
17
|
+
post_index = 0
|
18
|
+
no_more_posts = false
|
19
|
+
|
20
|
+
while !no_more_posts
|
21
|
+
Tumblr.posts(blog, password = options[:password], :start => post_index, :type => options[:filter]) do |posts, total_posts|
|
22
|
+
post_index += posts.count
|
23
|
+
no_more_posts = posts.empty? || post_index >= total_posts
|
24
|
+
|
25
|
+
exited = false
|
26
|
+
|
27
|
+
posts.each do |post|
|
28
|
+
unless exited
|
29
|
+
post_status = options[:automatically_add_each_post] ? :y : yield(post)
|
30
|
+
|
31
|
+
if post_status == :y || post_status == :s
|
32
|
+
post_created = post.add_to_dayone!(starred = post_status == :s, dayone_journal = options[:journal_path])
|
33
|
+
|
34
|
+
puts "ERROR: There was a problem adding the post." unless post_created
|
35
|
+
elsif post_status == :exit
|
36
|
+
exited = true
|
37
|
+
no_more_posts = true
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
rescue TumblrPostsAPIError => e
|
44
|
+
puts e.message
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
module Tumblr
|
51
|
+
class Post
|
52
|
+
|
53
|
+
def add_to_dayone!(starred = false, dayone_journal = nil)
|
54
|
+
|
55
|
+
post_created = false
|
56
|
+
|
57
|
+
photo = nil
|
58
|
+
|
59
|
+
begin
|
60
|
+
|
61
|
+
if self.photo_url
|
62
|
+
photo = Tempfile.new(["#{self.photo_url.hash}", File.extname(self.photo_url)])
|
63
|
+
photo.open
|
64
|
+
photo.write open(self.photo_url).read
|
65
|
+
photo.close
|
66
|
+
end
|
67
|
+
|
68
|
+
post_created = Dayone.create_post(self.full_body,
|
69
|
+
:date => self.created_at,
|
70
|
+
:starred => starred,
|
71
|
+
:photo_path => photo ? photo.path : nil,
|
72
|
+
:journal_path => dayone_journal
|
73
|
+
)
|
74
|
+
|
75
|
+
ensure
|
76
|
+
if photo
|
77
|
+
photo.close
|
78
|
+
photo.unlink
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
post_created
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tumblr_to_dayone/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tumblr_to_dayone"
|
8
|
+
spec.version = TumblrToDayone::VERSION
|
9
|
+
spec.authors = ["Joey Meyer"]
|
10
|
+
spec.email = ["jmeyer41@gmail.com"]
|
11
|
+
spec.description = "Command prompt which makes it easy to add Tumblr posts to your Day One journal."
|
12
|
+
spec.summary = "A simple command prompt that easily allows you to add posts from a Tumblr blog to your Day One journal."
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
|
24
|
+
spec.add_dependency "reverse_markdown", "~> 0.4"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tumblr_to_dayone
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joey Meyer
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-30 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: reverse_markdown
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.4'
|
55
|
+
description: Command prompt which makes it easy to add Tumblr posts to your Day One
|
56
|
+
journal.
|
57
|
+
email:
|
58
|
+
- jmeyer41@gmail.com
|
59
|
+
executables:
|
60
|
+
- tumblr_to_dayone
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- .gitignore
|
65
|
+
- Gemfile
|
66
|
+
- LICENSE.txt
|
67
|
+
- README.md
|
68
|
+
- Rakefile
|
69
|
+
- bin/tumblr_to_dayone
|
70
|
+
- lib/tumblr_to_dayone.rb
|
71
|
+
- lib/tumblr_to_dayone/dayone.rb
|
72
|
+
- lib/tumblr_to_dayone/prompt.rb
|
73
|
+
- lib/tumblr_to_dayone/tumblr.rb
|
74
|
+
- lib/tumblr_to_dayone/tumblr/post.rb
|
75
|
+
- lib/tumblr_to_dayone/version.rb
|
76
|
+
- tumblr_to_dayone.gemspec
|
77
|
+
homepage: ''
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ! '>='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ! '>='
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.0.5
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: A simple command prompt that easily allows you to add posts from a Tumblr
|
101
|
+
blog to your Day One journal.
|
102
|
+
test_files: []
|