Skip to content

Commit

Permalink
Initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
peanball committed May 25, 2022
0 parents commit 69e22cf
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 0 deletions.
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# MS Teams Meetings Launcher in SwiftBar

This plugin checks your calendar for upcoming MS Teams meetings and links them with direct links to the MS Teams desktop client. No more weird redirects through the browser, just open the meeting right away.

## Features

* Finds calendar entries for the current day that contain links to https://teams.microsoft.com and turns them to `msteams:` links that are opened by the MS Teams Desktop client right away
* Indicates an upcoming meeting 15 min before it starts with the name in the toolbar
* Shows you the name of the currently running meeting
* Warns you about a meeting that is about to start and turns the item to a direct link for this meeting
* Shows you the the warning about the next meeting, even if there is a meeting running (back to back)

## Prerequisites

1. SwiftBar. This will likely also work with XBar but I have not tried.
2. `icalBuddy`.
Usually this can be installed via `brew install ical-buddy`, but the version there is not working correctly in SwiftBar.
See below in Troubleshooting.

## Troubleshooting

* SwiftBar needs to have Calendar permissions. The current version 1.4.3 (and 1.4.4 beta) were not asking for permission. Version 1.2.1 did ask for permission.
* The version currently (2022-05-25) installed via Homebrew has a bug that will not ask for Calendar access and will not show any results. A pre-built version [in the KeyboardMaestro Forum](https://forum.keyboardmaestro.com/t/icalbuddy-doesnt-work-within-keyboard-maestro-mojave-calendar-permissions/15446/6) works fine. Install at your own risk. There is hope that the upstream maintainer of the Homebrew release will include the patch soon and I can remove this section.
134 changes: 134 additions & 0 deletions teams.15s.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
#!/usr/bin/env python3

# metadata
# <xbar.title>Teams Next Meetings</xbar.title>
# <xbar.version>v0.1</xbar.version>
# <xbar.author>Alexander Lais</xbar.author>
# <xbar.author.github>peanball</xbar.author.github>
# <xbar.desc>Next Team Meetings</xbar.desc>
# <xbar.abouturl>https://github.com/peanball/teams-swiftbar/README.md<xbar.abouturl>
# <xbar.image></xbar.image>
# <bitbar.dependencies>python3, icalBuddy(fixed)</bitbar.dependencies>
# <swiftbar.runInBash>true</swiftbar.runInBash>

import datetime
import os

import re
from dateutil import parser

from urllib.parse import unquote

BULLET = "__BULLET__"

LOCAL_TIMEZONE = datetime.datetime.now(datetime.timezone.utc).astimezone().tzinfo
now = datetime.datetime.now(tz=LOCAL_TIMEZONE)

cmd = (
f"icalBuddy -b '{BULLET}' -nnr "
' -tf "%Y-%m-%dT%H:%M:%S.000%z" -nc -iep "title,datetime,notes" -ps "|\t|" eventsToday '
'| tr "\r" " "'
)

teams_link = r"(?: |<|&lt;)https://teams.microsoft.com(.*?)(?:>|&gt;| )"

teams_meetings = []

with os.popen(cmd) as result:
entries = [entry.strip() for entry in result.readlines()]

for entry in entries:
split = entry.split("\t")
link = None

if len(split) < 3:
continue

name, notes, time = split
name = name.replace(BULLET, "")
startDate, endDate = re.sub(r"^.* at", "", time).split(" - ")
end = parser.parse(endDate)
if end < now:
continue

start = parser.parse(startDate)

links = re.findall(teams_link, notes)
if links:
link = f"msteams:{unquote(links[0])}"

teams_meetings.append(
[
name,
start,
end,
link,
]
)

if not teams_meetings:
exit(0)

running_meeting = [m for m in teams_meetings if m[1] <= now and m[2] > now]

# time in minutes when a 'countdown' is shown in the item's main text
upcoming_time = 15
# time in minutes when a 'countdown' is shown with a link to the meeting in the item's main text
pending_time = 5

upcoming_meeting = [
m for m in teams_meetings if m[1] > now and (m[1] - now) < datetime.timedelta(minutes=upcoming_time) and (m[1] - now) > datetime.timedelta(minutes=pending_time)
]

pending_meeting = [
m for m in teams_meetings if m[1] > now and (m[1] - now) < datetime.timedelta(minutes=pending_time)
]

def format_duration(seconds):
words = ["y", "d", "h", "m", "s"]

if not seconds:
return "now"
else:
m, s = divmod(seconds, 60)
h, m = divmod(m, 60)
d, h = divmod(h, 24)
y, d = divmod(d, 365)

time = [y, d, h, m, s]

duration = []

for x, i in enumerate(time):
if i > 0:
duration.append(f"{int(i)}{words[x]}")

return "".join(duration)

by_start=lambda m: m[1]

if pending_meeting:
(name, start, end, link) = sorted(pending_meeting, key=by_start)[0]
timespan = (start - now).total_seconds() - (start - now).total_seconds() % 60
time_to_meeting = format_duration(timespan)
print(
f":rectangle.3.group.bubble.left.fill: {name} in {time_to_meeting} | sfcolor=#CC0000,#FF3300 href={link}"
)
elif running_meeting:
(name, start, end, link) = sorted(running_meeting, key=by_start)[0]
print(f":rectangle.3.group.bubble.left.fill: {name} | href={link}")
elif upcoming_meeting:
(name, start, end, link) = sorted(upcoming_meeting, key=by_start)[0]
timespan = (start - now).total_seconds() - (start - now).total_seconds() % 60
time_to_meeting = format_duration(timespan)
print(f":rectangle.3.group.bubble.left.fill: {name} in {time_to_meeting}")
else:
print(":rectangle.3.group.bubble.left:")
print("---")

for (name, start, end, link) in sorted(teams_meetings, key=by_start):
starttime = start.strftime("%H:%M")
endtime = end.strftime("%H:%M")
duration = format_duration((end - start).total_seconds())

print(f"{name.strip()} - {starttime} ({duration}) | href={link}")

0 comments on commit 69e22cf

Please sign in to comment.