Module: Timely::Notifications

Defined in:
lib/timely/notifications.rb

Class Method Summary collapse

Class Method Details

.check_and_notify(db, default_alarm: 15) ⇒ Object

Check for upcoming events and send desktop notifications. alarm_minutes: how many minutes before event to notify.



5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/timely/notifications.rb', line 5

def self.check_and_notify(db, default_alarm: 15)
  now = Time.now.to_i
  # Check events starting within the next hour
  upcoming = db.get_events_in_range(now, now + 3600)

  # Ensure notification_log table exists
  ensure_notification_table(db)

  upcoming.each do |evt|
    next if evt['all_day'].to_i == 1

    start_ts = evt['start_time'].to_i
    minutes_until = (start_ts - now) / 60

    # Get alarm offsets (default to configured alarm if not set)
    alarms = evt['alarms']
    alarms = JSON.parse(alarms) if alarms.is_a?(String)
    alarm_offsets = alarms.is_a?(Array) ? alarms : [default_alarm]

    alarm_offsets.each do |offset|
      offset = offset.to_i
      # Trigger if we are within 1 minute of the alarm time
      next unless minutes_until >= offset - 1 && minutes_until <= offset + 1

      # Check if we already notified for this alarm
      notified = db.db.get_first_value(
        "SELECT COUNT(*) FROM notification_log WHERE event_id = ? AND alarm_offset = ?",
        [evt['id'], offset]
      ).to_i > 0

      unless notified
        send_notification(evt, minutes_until)
        db.db.execute(
          "INSERT OR REPLACE INTO notification_log (event_id, alarm_offset, notified_at) VALUES (?, ?, ?)",
          [evt['id'], offset, now]
        )
      end
    end
  end
rescue => e
  # Never crash on notification errors
  nil
end

.ensure_notification_table(db) ⇒ Object

Create the notification_log table if it does not exist.



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/timely/notifications.rb', line 66

def self.ensure_notification_table(db)
  return if @notification_table_created
  db.db.execute <<-SQL
    CREATE TABLE IF NOT EXISTS notification_log (
      event_id INTEGER NOT NULL,
      alarm_offset INTEGER NOT NULL,
      notified_at INTEGER NOT NULL,
      PRIMARY KEY(event_id, alarm_offset)
    )
  SQL
  # Clean old entries (older than 24 hours)
  db.db.execute("DELETE FROM notification_log WHERE notified_at < ?", [Time.now.to_i - 86400])
  @notification_table_created = true
rescue => e
  nil
end

.send_notification(evt, minutes_until) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/timely/notifications.rb', line 49

def self.send_notification(evt, minutes_until)
  title = evt['title'] || '(No title)'
  time_str = Time.at(evt['start_time'].to_i).strftime('%H:%M')
  body = if minutes_until <= 1
    "Starting now (#{time_str})"
  else
    "In #{minutes_until.to_i} minutes (#{time_str})"
  end
  loc = evt['location']
  body += "\n#{loc}" if loc && !loc.to_s.empty?

  system("notify-send", "-a", "Timely", "-u", "normal", "-i", "calendar", title, body)
rescue => e
  nil
end