Just for fun, every time I go to the gym I would like to award myself a workout badge. This is the final post in this little series, please checkout the previous posts for some more details.
In this post I’ll be creating a ruby file containing the code to check my online Gold’s Gym account, and code to talk to my Discourse instance and award myself a badge if I went to the gym that day.
Then I’ll create a cron job on my server to run daily that will call my ruby script.
One issue I realized when setting up the cron job is that my server is in UTC time, but I want the script to run at the end of the day Mountain Time. At first I thought I would have to handle this in the cron job somehow, but actually I can specify the date I pass to Gold’s Gym in ruby based on a specific timezone using the tzinfo
gem.
@tz = TZInfo::Timezone.get('US/Mountain')
Time.now.getlocal(@tz.current_period.offset.utc_total_offset)
The golds check-in ruby script
For the golds check-in ruby script I setup two classes, GoldsCheckin and DiscourseBadge. At the bottom of the script I run golds_checkin.fetch_checkins
and if I have a checkin for that day I assign myself a badge with discourse.grant_workout_badge
:
require 'yaml'
require 'net/http'
require 'http-cookie'
require 'cgi'
require 'json'
require 'tzinfo'
require 'discourse_api'
class GoldsCheckin
def initialize
@config = YAML.load_file('config.yml')
@jar = HTTP::CookieJar.new
@tz = TZInfo::Timezone.get('US/Mountain')
end
def login
uri = URI('https://mico.myiclubonline.com/iclub/j_spring_security_check')
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Post.new uri
req.form_data = { "j_username": @config['username'], "j_password": @config['password'] }
res = http.request req
res.get_fields('Set-Cookie').each do |value|
@jar.parse(value, req.uri)
end
end
end
def fetch_checkins
today = Time.now.getlocal(@tz.current_period.offset.utc_total_offset)
low_date = CGI::escape(today.strftime("%m/%d/%Y"))
high_date = CGI::escape(today.strftime("%m/%d/%Y"))
uri = URI("https://mico.myiclubonline.com/iclub/account/checkInHistory.htm?lowDate=#{low_date}&highDate=#{high_date}")
Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
req = Net::HTTP::Get.new(uri)
req['Cookie'] = HTTP::Cookie.cookie_value(@jar.cookies(uri))
res = http.request req
res.body
end
end
end
class DiscourseBadge
def initialize
@config = YAML.load_file('discourse_config.yml')
@client = DiscourseApi::Client.new(@config['host'])
@client.api_key = @config['api_key']
@client.api_username = @config['api_username']
end
def grant_workout_badge
badge = {
badge_id: 104,
username: 'blake'
}
@client.grant_user_badge(badge)
end
end
golds_checkin = GoldsCheckin.new
discourse = DiscourseBadge.new
golds_checkin.login
response = golds_checkin.fetch_checkins
json = JSON.parse(response)
if json.count > 0
discourse.grant_workout_badge
end
Create a script to call the ruby app
Now that we have our little ruby app let’s create a bash script that we can easily call from our cron job that will call our ruby app.
Create a new file in /usr/bin
and call it workout
with the following contents:
#!/bin/bash
cd /golds_checkin && ruby app.rb
Now make sure our bash script is executable with:
chmod +x /usr/bin/workout
Create a cron job:
Now to finish it all off let’s create a cron job that will call our bash script. To create/edit a cron job run crontab -e
:
0 5 * * * /usr/bin/workout
This should run our script once per day at 5 AM UTC, which will actually be 11:00 PM MST (the previous day). Usually I don’t go to gym that late, but this will capture if I ever go to gym in the evening after work.