init
commit
f52cfe1117
|
@ -0,0 +1,13 @@
|
||||||
|
|
||||||
|
DB_USER=
|
||||||
|
DB_PASSWORD=
|
||||||
|
DB_HOST=
|
||||||
|
DB_PORT=
|
||||||
|
DB_NAME=
|
||||||
|
|
||||||
|
SMTP_SERVER=
|
||||||
|
SMTP_PORT=
|
||||||
|
SMTP_USERNAME=
|
||||||
|
SMTP_PASSWORD=
|
||||||
|
|
||||||
|
HUGGINGFACE_API_TOKEN=
|
|
@ -0,0 +1 @@
|
||||||
|
.env
|
|
@ -0,0 +1,7 @@
|
||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
gem 'pg'
|
||||||
|
gem 'net-smtp'
|
||||||
|
gem 'httparty'
|
||||||
|
gem 'dotenv'
|
||||||
|
gem 'erb'
|
|
@ -0,0 +1,36 @@
|
||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
bigdecimal (3.1.9)
|
||||||
|
cgi (0.4.2)
|
||||||
|
csv (3.3.2)
|
||||||
|
dotenv (3.1.7)
|
||||||
|
erb (4.0.4)
|
||||||
|
cgi (>= 0.3.3)
|
||||||
|
httparty (0.22.0)
|
||||||
|
csv
|
||||||
|
mini_mime (>= 1.0.0)
|
||||||
|
multi_xml (>= 0.5.2)
|
||||||
|
mini_mime (1.1.5)
|
||||||
|
multi_xml (0.7.1)
|
||||||
|
bigdecimal (~> 3.1)
|
||||||
|
net-protocol (0.2.2)
|
||||||
|
timeout
|
||||||
|
net-smtp (0.5.1)
|
||||||
|
net-protocol
|
||||||
|
pg (1.5.9)
|
||||||
|
timeout (0.4.3)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
arm64-darwin-24
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
dotenv
|
||||||
|
erb
|
||||||
|
httparty
|
||||||
|
net-smtp
|
||||||
|
pg
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.6.2
|
|
@ -0,0 +1,114 @@
|
||||||
|
require 'bundler'
|
||||||
|
Bundler.require(:default)
|
||||||
|
|
||||||
|
require 'pg'
|
||||||
|
require 'net/smtp'
|
||||||
|
require 'httparty'
|
||||||
|
require 'json'
|
||||||
|
require 'dotenv'
|
||||||
|
require 'erb'
|
||||||
|
require 'date'
|
||||||
|
require 'tempfile'
|
||||||
|
|
||||||
|
|
||||||
|
# Load environment variables from.env file
|
||||||
|
Dotenv.load
|
||||||
|
|
||||||
|
DB_USER = ENV.fetch('DB_USER')
|
||||||
|
DB_PASSWORD = ENV.fetch('DB_PASSWORD')
|
||||||
|
DB_HOST = ENV.fetch('DB_HOST')
|
||||||
|
DB_PORT = ENV.fetch('DB_PORT')
|
||||||
|
DB_NAME = ENV.fetch('DB_NAME')
|
||||||
|
|
||||||
|
SMTP_SERVER = ENV.fetch('SMTP_SERVER')
|
||||||
|
SMTP_PORT = ENV.fetch('SMTP_PORT').to_i
|
||||||
|
SMTP_USERNAME = ENV.fetch('SMTP_USERNAME')
|
||||||
|
SMTP_PASSWORD = ENV.fetch('SMTP_PASSWORD')
|
||||||
|
|
||||||
|
HUGGINGFACE_API_URL = 'https://router.huggingface.co/hf-inference/models/mistralai/Mixtral-8x7B-Instruct-v0.1/v1/chat/completions'
|
||||||
|
HUGGINGFACE_API_TOKEN = ENV.fetch('HUGGINGFACE_API_TOKEN')
|
||||||
|
|
||||||
|
MAIL_BODY_PROMPT = <<-END
|
||||||
|
Analyze my recent weeks listening history and suggest a movie/comic character or celebrity that embodies the mood and spirit of my diverse music choices. Please keep the response concise and engaging, suitable for a marketing email.
|
||||||
|
Here are the titles that were listened to this week
|
||||||
|
|
||||||
|
END
|
||||||
|
|
||||||
|
def fetch_titles_from_db()
|
||||||
|
one_week_ago = Date.today - 7
|
||||||
|
connection = PG.connect(:dbname => DB_NAME, :host => DB_HOST, :user => DB_USER, :password => DB_PASSWORD)
|
||||||
|
|
||||||
|
result = connection.exec_params("SELECT item_title FROM activities WHERE created_at >= $1 ORDER BY created_at DESC", [one_week_ago])
|
||||||
|
connection.close
|
||||||
|
results = []
|
||||||
|
results = result.map { |row| row['item_title']} if result.any?
|
||||||
|
return results
|
||||||
|
end
|
||||||
|
|
||||||
|
def generate_email_body(titles)
|
||||||
|
return "You didnt listen to any music this week :`(" unless titles
|
||||||
|
|
||||||
|
puts "Calling HF"
|
||||||
|
body = mistral_request_body(titles)
|
||||||
|
response = HTTParty.post(HUGGINGFACE_API_URL, headers: { 'Content-Type':'application/json','Authorization' => "Bearer #{HUGGINGFACE_API_TOKEN}" }, body: body)
|
||||||
|
JSON.parse(response.body)['choices'][0]['message']['content']
|
||||||
|
end
|
||||||
|
|
||||||
|
def mistral_request_body(titles)
|
||||||
|
formated_titles = titles.join(", ")
|
||||||
|
{
|
||||||
|
model: "mistralai/Mixtral-8x7B-Instruct-v0.1",
|
||||||
|
messages: [
|
||||||
|
{
|
||||||
|
role: "user",
|
||||||
|
content: "#{MAIL_BODY_PROMPT} #{formated_titles}"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
max_tokens: 1000,
|
||||||
|
stream: false
|
||||||
|
}.to_json
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_email(to, subject, body)
|
||||||
|
current_time = Time.now.utc
|
||||||
|
formatted_time = current_time.strftime("%a, %-d %b %Y %H:%M:%S %z")
|
||||||
|
|
||||||
|
msg = <<-END
|
||||||
|
Subject: #{subject}
|
||||||
|
From: Weekly 1activity <#{SMTP_USERNAME}>
|
||||||
|
To: #{to}
|
||||||
|
Date: #{formatted_time}
|
||||||
|
MIME-Version: 1.0
|
||||||
|
Content-Type: text/html; charset=utf-8
|
||||||
|
|
||||||
|
#{body}
|
||||||
|
END
|
||||||
|
|
||||||
|
smtp = Net::SMTP.new(SMTP_SERVER, SMTP_PORT)
|
||||||
|
smtp.enable_starttls_auto
|
||||||
|
smtp.start do |server|
|
||||||
|
a = server.authenticate(SMTP_USERNAME, SMTP_PASSWORD)
|
||||||
|
puts a.string
|
||||||
|
begin
|
||||||
|
a= server.send_message(msg, SMTP_USERNAME, to)
|
||||||
|
puts a.string
|
||||||
|
rescue Net::SMTPFatalError => e
|
||||||
|
puts e.message
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def main
|
||||||
|
titles = fetch_titles_from_db
|
||||||
|
total_plays = titles.length
|
||||||
|
body = generate_email_body(titles)
|
||||||
|
puts "HF done"
|
||||||
|
template = ERB.new File.read('./template/mail.html.erb')
|
||||||
|
html_body = template.result_with_hash({body: body, total_plays: total_plays})
|
||||||
|
title = "Weekly listening report #{Date.today}"
|
||||||
|
send_email("listening@sgui.de", title, html_body)
|
||||||
|
puts "Email sent with subject: #{title}"
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
main
|
|
@ -0,0 +1,38 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang/en>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Weekly listening report <%= Date.today.to_s %></title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Arial, sans-serif; line-height: 1.5; margin: 0; padding: 0; }
|
||||||
|
.container { max-width: 600px; margin: auto; }
|
||||||
|
.header { background-color: #1DB954; color: #fff; padding: 20px; text-align: center; font-size: 24px; }
|
||||||
|
.content { padding: 20px; }
|
||||||
|
h1 { font-size: 32px; margin-top: 0; }
|
||||||
|
p { margin-bottom: 15px; }
|
||||||
|
.cta { background-color: #1DB954; border: none; border-radius: 5px; color: #fff; cursor: pointer; display: inline-block; font-size: 16px; padding: 10px 20px; text-decoration: none; }
|
||||||
|
.cta:hover { background-color: #1ED760; }
|
||||||
|
.footer { background-color: #f5f5f5; padding: 20px; text-align: center; font-size: 12px; color: #777; }
|
||||||
|
.footer a { color: #1DB954; text-decoration: none; }
|
||||||
|
.footer a:hover { text-decoration: underline; }
|
||||||
|
@media only screen and (max-width: 600px) {
|
||||||
|
.container { width: 100%; }
|
||||||
|
.header,.footer { text-align: left; }
|
||||||
|
h1 { font-size: 28px; }
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">Your listening report for <%= Date.today.to_s %></div>
|
||||||
|
<div class="content">
|
||||||
|
<h1>Heyo!</h1>
|
||||||
|
<p>Total plays <%= total_plays %></p>
|
||||||
|
<p><%= body %></p>
|
||||||
|
<a href="#" class="cta">Listen Now</a>
|
||||||
|
</div>
|
||||||
|
<div class="footer"><a href="#">Unsubscribe</a> | <a href="#">Privacy Policy</a></div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue