Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion shard.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ targets:
bootstrap:
main: src/main.cr

crystal: 1.11.2
crystal: ">= 1.20.0"

license: MIT
2 changes: 1 addition & 1 deletion src/error/usecase.cr
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ module Error
pretext: "<@#{ENV["SLACK_ID"]}> #{message}",
color: "#EB4646",
title: err.message,
text: err.backtrace.join('\n'),
text: err.backtrace?.try(&.join('\n')),
footer: "github_notifications_slack (#{ENV["ENV"]})",
footer_icon: "",
)
Expand Down
84 changes: 40 additions & 44 deletions src/github/models.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,34 @@ module Github
class Notifications
include JSON::Serializable

property subject : Subject
property reason : String
property repository : Repository
property subscription_url : String?
MENTION_REASONS = {
"assign",
"author",
"comment",
"invitation",
"mention",
"team_mention",
"review_requested",
# "ci_activity",
}

getter subject : Subject
getter reason : String
getter repository : Repository
getter subscription_url : String?

def mention? : Bool
[
"assign",
"author",
"comment",
"invitation",
"mention",
"team_mention",
"review_requested",
# "ci_activity",
].includes?(reason)
reason.in?(MENTION_REASONS)
end
end

class Subject
include JSON::Serializable

property type : String
property title : String?

@[JSON::Field(emit_null: false)]
property url : String = ""

@[JSON::Field(emit_null: false)]
property latest_comment_url : String = ""
getter type : String
getter title : String?
getter url : String = ""
getter latest_comment_url : String = ""

module Type
PULL_REQUEST = "PullRequest"
Expand All @@ -42,13 +40,15 @@ module Github
DISCUSSION = "Discussion"
end

UPDATE_TYPES = {
Type::PULL_REQUEST,
Type::ISSUE,
Type::COMMIT,
Type::DISCUSSION,
}

def update? : Bool
[
Type::PULL_REQUEST,
Type::ISSUE,
Type::COMMIT,
Type::DISCUSSION,
].includes?(type)
type.in?(UPDATE_TYPES)
end

def color : String
Expand All @@ -67,28 +67,24 @@ module Github
end

def comment_url : String
if !latest_comment_url.blank?
latest_comment_url
else
url
end
latest_comment_url.presence || url
end
end

class Repository
include JSON::Serializable

property full_name : String?
property html_url : String?
property owner : User
getter full_name : String?
getter html_url : String?
getter owner : User
end

class Comment
include JSON::Serializable

property user : User
property html_url : String?
property body : String?
getter user : User
getter html_url : String?
getter body : String?

def initialize(@body)
@user = User.new
Expand All @@ -98,9 +94,9 @@ module Github
class User
include JSON::Serializable

property login : String?
property avatar_url : String?
property html_url : String?
getter login : String?
getter avatar_url : String?
getter html_url : String?

def initialize
end
Expand All @@ -109,7 +105,7 @@ module Github
class Error
include JSON::Serializable

property message : String
property documentation_url : String?
getter message : String
getter documentation_url : String?
end
end
24 changes: 12 additions & 12 deletions src/github/repository.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,20 @@ module Github

def find_notifications_unread : Array(Notifications)
res = @github.get "/notifications"
if res.status_code >= 500
if res.status.server_error?
Serverless::Lambda.print_log "return 5xx error from notifications api"
return Array(Notifications).new
elsif res.status_code == 401
return [] of Notifications
elsif res.status.unauthorized?
# GitHub が断続的に 401 を返すことがあるため、毎分の次回実行に任せてスキップする。
# トークン失効などの恒久的な 401 までサイレントに握りつぶす点は本来リトライや
# 連続失敗の監視で区別すべきだが、個人用途の通知ツールであり実装コストに
# 見合わないため割り切る。
Serverless::Lambda.print_log "return 401 error from notifications api, skip"
return Array(Notifications).new
elsif res.status_code >= 400
return [] of Notifications
elsif res.status.client_error?
Serverless::Lambda.print_log "return 4xx error from notifications api"
err = Error.from_json res.body
raise "notifications api retrun client error: #{err.message}"
raise "notifications api return client error: #{err.message}"
end

Serverless::Lambda.print_log "notifications body: #{res.body}"
Expand All @@ -43,21 +43,21 @@ module Github
end

res = @github.get url
if res.status_code >= 500
if res.status.server_error?
Serverless::Lambda.print_log "return 5xx error from comments api"
return Comment.new "comments api retrun server error"
elsif res.status_code >= 400
return Comment.new "comments api return server error"
elsif res.status.client_error?
Serverless::Lambda.print_log "return 4xx error from comments api"
err = Error.from_json res.body
return Comment.new "comments api retrun client error: #{err.message}"
return Comment.new "comments api return client error: #{err.message}"
end

begin
Serverless::Lambda.print_log "comment body: #{res.body}"
Comment.from_json res.body
rescue
Serverless::Lambda.print_log "faild parse comment data"
Comment.new "faild parse comment data"
Serverless::Lambda.print_log "failed parse comment data"
Comment.new "failed parse comment data"
end
end

Expand Down
2 changes: 1 addition & 1 deletion src/github/usecase.cr
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module Github
title: notify.subject.title,
title_link: comment.html_url,
text: comment.body,
footer: !notify.repository.full_name.nil? ? notify.repository.full_name : "github",
footer: notify.repository.full_name || "github",
footer_icon: notify.repository.owner.avatar_url,
)
end
Expand Down
2 changes: 1 addition & 1 deletion src/notify/usecase.cr
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ module Notify
github_uc.to_slack_attachment item, pretext, message
end

if notices.size != 0
unless notices.empty?
slack_repo = Slack::PostRepository.new ENV["WEBHOOK_URL"]
slack_repo.send_attachments notices

Expand Down
16 changes: 12 additions & 4 deletions src/runtime/lambda.cr
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
require "json"
require "log"
require "http/client"

module Serverless
module Lambda
extend self

def handler(name : String)
Log = ::Log.for("lambda")

def handler(name : String, &)
return if name != ENV["_HANDLER"]

ENV["SSL_CERT_FILE"] = "/etc/pki/tls/cert.pem"
Expand All @@ -32,10 +35,15 @@ module Serverless
end
end

# CloudWatch では改行ごとにログエントリが分割されるため、改行を除去して
# 1 エントリにまとめつつ、長い本文は適度なチャンクに分割して出力する。
# 中間の Array(Char) を作らずに済むよう文字列スライスで分割する。
def print_log(log : String)
log.split(//).each_slice(50000) do |line|
puts `echo '#{line.join.gsub(/(\r\n|\r|\n|\f)/, "")}'`
STDOUT.flush
cleaned = log.gsub(/(\r\n|\r|\n|\f)/, "")
offset = 0
while offset < cleaned.size
Log.info { cleaned[offset, 50000] }
offset += 50000
end
end
Comment thread
limit7412 marked this conversation as resolved.
end
Expand Down
26 changes: 13 additions & 13 deletions src/slack/models.cr
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,17 @@ module Slack
class Attachment
include JSON::Serializable

property fallback : String?
property author_name : String?
property author_icon : String?
property author_link : String?
property pretext : String?
property color : String?
property title : String?
property title_link : String?
property text : String?
property footer : String?
property footer_icon : String?
getter fallback : String?
getter author_name : String?
getter author_icon : String?
getter author_link : String?
getter pretext : String?
getter color : String?
getter title : String?
getter title_link : String?
getter text : String?
getter footer : String?
getter footer_icon : String?

def initialize(
@fallback = nil,
Expand All @@ -27,15 +27,15 @@ module Slack
@title_link = nil,
@text = nil,
@footer = nil,
@footer_icon = nil
@footer_icon = nil,
)
end
end

class Post
include JSON::Serializable

property attachments : Array(Attachment)
getter attachments : Array(Attachment)

def initialize(@attachments)
end
Expand Down
Loading