├── .gitignore ├── LICENSE ├── README.md ├── clean.rb ├── journal.rb ├── obsidian.css ├── recent.rb └── todo.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Mike McClenaghan 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Obsidian Utils 2 | 3 | Various scripts and utilities for Obsidian.md 4 | 5 | ## Usage 6 | 7 | ### journal.rb 8 | 9 | To run: 10 | 11 | ``` 12 | ruby journal.rb 13 | ``` 14 | 15 | Journal.rb simply creates a file following the `YYYY-MM-DD.md` naming convention that Obsidian uses for daily notes. This file will be created in your notes directory following a template that's listed in the script. Currently, it pulls some weather data from Environment Canada, adds some boilerplate tasks for the day, and embeds links to the previous five days of daily notes. 16 | 17 | Note that it will not overwrite the daily note for today if it already exists. It will only ever append to it. 18 | 19 | ### todo.rb 20 | 21 | To run: 22 | 23 | ``` 24 | ruby todo.rb 25 | ``` 26 | 27 | Todo.rb will loop through all of the markdown files in your notes directory (including subdirectories) to find any lines that follow the syntax for empty checklists. Then it writes them all (as a copy, not an embed) to a file called `TODO.md` in your notes directory. 28 | 29 | This script will work for the `*`, `-`, and `+` bullet syntaxes in Markdown. 30 | 31 | ``` 32 | * [ ] This is a todo 33 | - [ ] This is a todo 34 | + [ ] This is a todo 35 | ``` 36 | 37 | **NOTE** - this script overwrites the `TODO.md` file each time it is run. If you manually write any notes to `TODO.md` and then run this script, those notes will be lost. 38 | 39 | ### clean.rb 40 | 41 | To run: 42 | 43 | ``` 44 | ruby clean.rb 45 | ``` 46 | 47 | Clean.rb will loop through all ofthe markdown files in your notes directory (including subdirectories) to find any files that are both 1) empty (filesize = zero), and 2) unlinked in any other note. If it finds notes that fit those criteria, it deletes them from your filesystem. 48 | 49 | **NOTE** - this script is intentionally destructive! It is highly recommended that you have your notes under version control or backed up somehow before runing this script. Cleaned files are not moved to the trash - they are immediately deleted! 50 | 51 | ## How to run Ruby scripts 52 | 53 | You'll need to have ruby installed on your machine. Type `ruby -v` to see if it's already set up. If you need to install it, check out https://www.ruby-lang.org/en/documentation/installation/ 54 | 55 | Once you have ruby installed, you should be able to execute any of the scripts by going to the directory where the script is stored and typing `ruby scriptname.rb`, replacing `scriptname.rb` with the actual name of the script i.e. `journal.rb` or `todo.rb`. 56 | 57 | ## Notes 58 | 59 | - Back up your vault before executing any of these scripts! They might be destructive and could wipe out all of your notes. 60 | - I keep all of my notes in one folder that's in a `Notes` folder in my home folder. I try to keep a variable for the `notes_path` in each of the scripts that you can edit for your local setup. 61 | 62 | ## Disclaimer 63 | 64 | Note that I've built all of these for my own use. Weather is for my local city, pathing is for my machine. You may need to fork the repo and update the scripts for your own use. Buyer beware! I try to make the scripts as safe as I can, but I won't take responsibility if your notes get deleted or overwritten by these scripts. 65 | -------------------------------------------------------------------------------- /clean.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def read_markdown_directory(raw_directory) 4 | links = [] 5 | rbfiles = File.join("**", "*.md") 6 | Dir.glob(rbfiles, base:raw_directory).each do |filename| 7 | links.push File.read(File.join(raw_directory,filename)).scan(/\[\[([^\]]*)\]\]/).flatten 8 | end 9 | links.flatten 10 | end 11 | 12 | def clean_empty_files(raw_directory, links_list) 13 | rbfiles = File.join("**", "*.md") 14 | Dir.glob(rbfiles, base:raw_directory).each do |filename| 15 | if File.zero?(File.join(raw_directory,filename)) 16 | unless links_list.find {|link| link == filename.chomp('.md')} 17 | puts "Deleting #{filename}" 18 | File.delete(File.join(raw_directory,filename)) 19 | end 20 | end 21 | end 22 | end 23 | 24 | notes_path = "#{ENV['HOME']}/Notes/" 25 | 26 | links_list = read_markdown_directory(notes_path) 27 | clean_empty_files(notes_path, links_list) 28 | -------------------------------------------------------------------------------- /journal.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'date' 3 | 4 | filepath = "#{ENV['HOME']}/Notes/" 5 | today = Date.today 6 | filename = today.strftime('%Y-%m-%d.md') 7 | last_year = today.prev_year.strftime('%Y-%m-%d') 8 | this_week = today.strftime('%G-W%V') 9 | this_week_monday = today - (today.cwday-1) 10 | week_filename = this_week + '.md' 11 | 12 | weather_raw = `curl -s https://weather.gc.ca/rss/city/ab-50_e.xml` 13 | today_weather_raw = weather_raw.split('').slice(3..4).join 14 | today_weather_list = today_weather_raw.scan(/.*(.*)<\/title>/).flatten 15 | 16 | journal_template = <<~JOURNAL 17 | # #{today.strftime('%A, %b %d, %Y')} 18 | [[#{last_year}]] | [[#{this_week}]] 19 | 20 | ## Weather 21 | #{today_weather_list[0]} 22 | #{today_weather_list[1]} 23 | 24 | ## TODO 25 | - [ ] 🗓 Check calendar for scheduled events 26 | - 27 | 28 | ## Daily Notes 29 | - 30 | JOURNAL 31 | 32 | File.open("#{filepath}#{filename}", 'a') { |f| f.write "#{journal_template}" } 33 | 34 | puts "Wrote daily template to #{filepath}#{filename}" 35 | 36 | weekly_template = <<~WEEKLY 37 | # #{this_week} 38 | 39 | ## Big Three 40 | 1. 41 | 2. 42 | 3. 43 | 44 | ## Daily stuff 45 | - Monday, [[#{this_week_monday.strftime('%Y-%m-%d')}]] 46 | - Tuesday, [[#{this_week_monday.next_day.strftime('%Y-%m-%d')}]] 47 | - Wednesday, [[#{this_week_monday.next_day(2).strftime('%Y-%m-%d')}]] 48 | - Thursday, [[#{this_week_monday.next_day(3).strftime('%Y-%m-%d')}]] 49 | - Friday, [[#{this_week_monday.next_day(4).strftime('%Y-%m-%d')}]] 50 | - Saturday, [[#{this_week_monday.next_day(5).strftime('%Y-%m-%d')}]] 51 | - Sunday, [[#{this_week_monday.next_day(6).strftime('%Y-%m-%d')}]] 52 | 53 | ## Last week [[#{(today-7).strftime('%G-W%V')}]] 54 | - Tasks: 55 | - [ ] Add meetings from last week to contacts and events 56 | - [ ] Make sure all [[TODO]] items from last week were either closed or moved 57 | - [ ] Make sure there are no recurring items that somehow got marked as [[DONE]] 58 | - Biggest wins: 59 | - 60 | - How far did you get on last week's big 3? 61 | 1. 62 | 2. 63 | 3. 64 | - What worked? What didn't? 65 | - 66 | - What will you keep, improve, start, or stop based on the above? 67 | - 68 | WEEKLY 69 | 70 | unless File.exist?("#{filepath}#{week_filename}") 71 | File.open("#{filepath}#{week_filename}", 'w') { |f| f.write "#{weekly_template}" } 72 | puts "Wrote weekly template to #{filepath}#{week_filename}" 73 | end -------------------------------------------------------------------------------- /obsidian.css: -------------------------------------------------------------------------------- 1 | /* from https://github.com/kmaasrud/clean-theme-obsidian*/ 2 | 3 | /* Custom font */ 4 | .cm-s-obsidian { 5 | font-family: "Fira Code"; 6 | } 7 | 8 | /* Collapsing sidedocks */ 9 | .app-container.is-left-sidedock-collapsed .side-dock.mod-left:not(:hover), .app-container.is-right-sidedock-collapsed .side-dock.mod-right:not(:hover) { 10 | width: 0px !important; 11 | } 12 | 13 | /* Clean header */ 14 | .workspace-leaf-header { 15 | background-color: var(--background-primary) !important; 16 | border-bottom: 0px !important; 17 | } 18 | 19 | /* Cleaner status bar */ 20 | .status-bar { 21 | background-color: var(--background-primary); 22 | color: var(--text-faint); 23 | } 24 | 25 | 26 | /* Markdown syntax highlighting */ 27 | /* Header hashes */ 28 | .cm-formatting-header { 29 | color: var(--markdown-header-hash) !important; 30 | } 31 | 32 | .HyperMD-header .cm-formatting-header { 33 | color: #c2c2c2!important; 34 | opacity: .9; 35 | font-size: 80%; 36 | text-decoration: none; 37 | font-weight: 100; 38 | margin-right: -.5em; 39 | } 40 | 41 | /* Lists */ 42 | .cm-formatting-list { 43 | color: var(--markdown-list) !important; 44 | } 45 | 46 | 47 | /* --------------- */ 48 | /* Custom colors */ 49 | .theme-dark { 50 | --background-primary: #000; 51 | --background-secondary: #000; 52 | 53 | --text-normal: #dedcde; 54 | --text-accent: #94a8b3; 55 | --text-accent-hover: #83959e; 56 | } 57 | 58 | .theme-light { 59 | --text-accent: #6b8c9c; 60 | --text-accent-hover: #81a7b9; 61 | } 62 | -------------------------------------------------------------------------------- /recent.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'date' 3 | 4 | def read_markdown_directory(raw_directory) 5 | contents = {} 6 | recent_day_range = (Date.today.prev_day(7)..Date.today) 7 | recent_day_range.reverse_each{|date| contents[date.to_s] = []} 8 | rbfiles = File.join("**", "*.md") 9 | Dir.chdir(raw_directory) do 10 | Dir.glob(rbfiles).sort_by{ |f| File.mtime(f) }.reverse.each do |filename| 11 | if recent_day_range.cover?(File.mtime(filename).to_date) && !%w(RECENT.md TODO.md).include?(filename) 12 | contents[File.mtime(filename).strftime('%Y-%m-%d')] << filename.chomp('.md') 13 | end 14 | end 15 | end 16 | contents 17 | end 18 | 19 | def write_recent_file(filepath, recent_notes) 20 | File.open(filepath, 'w') do |file| 21 | file.write "# Recent Notes\n\n" 22 | recent_notes.each do |filename, lines| 23 | unless recent_notes[filename].empty? 24 | file.write "## #{filename}\n#{lines.map{|f| "- [[#{f}]]"}.join("\n")}\n\n" 25 | end 26 | end 27 | end 28 | end 29 | 30 | notes_path = "#{ENV['HOME']}/Notes/" 31 | recent_filepath = notes_path + 'RECENT.md' 32 | 33 | recent_notes = read_markdown_directory(notes_path) 34 | 35 | write_recent_file(recent_filepath, recent_notes) 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /todo.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | def read_markdown_directory(raw_directory) 4 | contents = {} 5 | rbfiles = File.join("**", "*.md") 6 | Dir.chdir(raw_directory) do 7 | Dir.glob(rbfiles).sort_by{ |f| File.mtime(f) }.reverse.each do |filename| 8 | unless filename == 'TODO.md' 9 | contents[filename.chomp('.md')] = File.read(filename, :encoding => 'utf-8') 10 | end 11 | end 12 | end 13 | contents 14 | end 15 | 16 | def extract_todo_lines(contents) 17 | todo_lines = {} 18 | contents.each do |filename, content| 19 | todo_text = '' 20 | content.scan(/[\*\-+]\s\[\s\]\s(.*)/).flatten.each do |match| 21 | todo_text += "- #{match} [[#{filename}]]\n" 22 | end 23 | todo_lines[filename] = todo_text unless todo_text.empty? 24 | end 25 | return todo_lines 26 | end 27 | 28 | def write_simple_todo_file(filepath, todo_lines) 29 | todo_notes = todo_lines.map{ |k,v| v }.join 30 | File.write(filepath, "# TODO\n\n#{todo_notes}") 31 | end 32 | 33 | def write_todo_file(filepath, todo_lines) 34 | File.open(filepath, 'w') do |file| 35 | file.write "# TODO\n\n" 36 | todo_lines.each do |filename, lines| 37 | file.write "## #{filename}\n\n#{lines}\n" 38 | end 39 | end 40 | end 41 | 42 | notes_path = "#{ENV['HOME']}/Notes/" 43 | todo_filepath = notes_path + 'TODO.md' 44 | 45 | notes = read_markdown_directory(notes_path) 46 | todo_lines = extract_todo_lines(notes) 47 | 48 | write_simple_todo_file(todo_filepath, todo_lines) 49 | 50 | 51 | 52 | --------------------------------------------------------------------------------