Initial commit

This commit is contained in:
Zachary Seguin 2017-05-28 20:03:18 -04:00
commit fec3bad50e
17 changed files with 1107 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
cscsysbot
ENV

27
ENV.sample Normal file
View File

@ -0,0 +1,27 @@
# IRC Configuration
export IRC_SERVER=chat.freenode.net
export IRC_PORT=6697
export IRC_CHANNELS="#channel"
export IRC_USER=user
export IRC_NICK=nick
export IRC_PASSWORD=password
# Set this variable to enable debug
export IRC_DEBUG
# Background Messages
export SYSCOM_CHANNELS="#channel"
# Twitter Configuration
export TWITTER_CONSUMER_KEY=""
export TWITTER_CONSUMER_SECRET=""
export TWITTER_ACCESS_TOKEN=""
export TWITTER_ACCESS_SECRET=""
export TWITTER_USERS="UWNetworkAlert"
# Uptime Robot
export UPTIME_ROBOT_API_KEY=""
# UPS
export UPSES=""
export UPS_COMMUNITY_STRING=""

38
cscsysbot.go Normal file
View File

@ -0,0 +1,38 @@
package main
import (
"os"
"strings"
"fmt"
"github.com/go-chat-bot/bot/irc"
// Plugins
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/hello"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/url"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/ups"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/member"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/club"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/ping"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/greetings"
// Backgrounds tasks
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/background"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/tweets"
_ "git.uwaterloo.ca/csc/cscsysbot/plugins/uptimerobot"
)
func main() {
_, debug := os.LookupEnv("IRC_DEBUG")
irc.Run(&irc.Config{
Server: fmt.Sprintf("%s:%s", os.Getenv("IRC_SERVER"), os.Getenv("IRC_PORT")),
Channels: strings.Split(os.Getenv("IRC_CHANNELS"), ","),
User: os.Getenv("IRC_USER"),
Nick: os.Getenv("IRC_NICK"),
Password: os.Getenv("IRC_PASSWORD"),
UseTLS: true,
TLSServerName: os.Getenv("IRC_SERVER"),
Debug: debug,
})
}

View File

@ -0,0 +1,32 @@
package background
import (
"os"
"strings"
"github.com/go-chat-bot/bot"
)
var Messages chan string = make(chan string, 100)
func processMessages(channel string) (string, error) {
select {
case message, _ := <- Messages:
return message, nil
default:
return "", nil
}
}
func init() {
channels := strings.Split(os.Getenv("SYSCOM_CHANNELS"), ",")
if len(channels) > 0 {
config := bot.PeriodicConfig{
CronSpec: "@every 3s",
Channels: channels,
CmdFunc: processMessages,
}
bot.RegisterPeriodicCommand("background_messages", config)
}
}

123
plugins/club/club.go Normal file
View File

@ -0,0 +1,123 @@
package club
import (
"crypto/tls"
"fmt"
"strconv"
"strings"
"sort"
"git.uwaterloo.ca/csc/cscsysbot/utils"
"github.com/go-chat-bot/bot"
"gopkg.in/ldap.v2"
)
const (
ldapServer = "ldap-master.csclub.uwaterloo.ca"
)
type ByTerm []string
func (s ByTerm) Len() int {
return len(s)
}
func (s ByTerm) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s ByTerm) Less(i, j int) bool {
a := s[i]
b := s[j]
ya, _ := strconv.ParseInt(a[1:5], 10, 32)
yb, _ := strconv.ParseInt(b[1:5], 10, 32)
if (ya == yb) {
// w, s, f (happens to be in reverse order)
return a[0] > b[0]
}
return ya < yb
}
func club(command *bot.Cmd) (string, error) {
var lines []string
if len(command.Args) != 1 {
return "An invalid number of arguments was provided. Usage is: !club userid", nil
}
authorized, _ := utils.SyscomNicks()
if (!utils.InList(authorized, command.User.Nick)) {
return "Sorry, you are not authorized to request membership information from me.", nil
}
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, 636), &tls.Config{
ServerName: ldapServer,
})
if err != nil {
return "", err
}
defer l.Close()
req := ldap.NewSearchRequest("ou=People,dc=csclub,dc=uwaterloo,dc=ca", ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(uid=%s)(objectClass=club))", command.Args[0]),
[]string{"*"}, nil)
res, err := l.Search(req)
if err != nil {
return "", err
}
if len(res.Entries) == 0 {
lines = append(lines, fmt.Sprintf("No clubs matched userid %q", command.Args[0]))
} else {
entry := res.Entries[0]
lines = append(lines, fmt.Sprintf("Club: %s (%s) -- %s",
entry.GetAttributeValue("uid"),
entry.GetAttributeValue("uidNumber"),
entry.GetAttributeValue("cn")))
lines = append(lines, fmt.Sprintf("Login Shell: %s, Home Directory: %s", entry.GetAttributeValue("loginShell"), entry.GetAttributeValue("homeDirectory")))
}
// Get members
req = ldap.NewSearchRequest("ou=Group,dc=csclub,dc=uwaterloo,dc=ca", ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(cn=%s)(objectClass=posixGroup))", command.Args[0]),
[]string{"*"}, nil)
res, err = l.Search(req)
if err != nil {
return "", err
}
if len(res.Entries) == 0 {
lines = append(lines, fmt.Sprintf("No groups matched name %q", command.Args[0]))
} else {
entry := res.Entries[0]
lines = append(lines, fmt.Sprintf("Group: %s (%s)", entry.GetAttributeValue("cn"), entry.GetAttributeValue("gidNumber")))
members := entry.GetAttributeValues("uniqueMember")
memberUids := make([]string, len(members))
for i, member := range members {
memberUids[i] = strings.Replace(strings.Replace(member, "uid=", "", -1), ",ou=People,dc=csclub,dc=uwaterloo,dc=ca", "", -1)
}
smemberUids := sort.StringSlice(memberUids[0:])
sort.Sort(smemberUids)
lines = append(lines, fmt.Sprintf("Members: %s", strings.Join(smemberUids, ", ")))
}
return strings.Join(lines, "\n"), nil
}
func init() {
bot.RegisterCommand(
"club",
"Prints club information",
"userid",
club)
}

View File

@ -0,0 +1,55 @@
package greetings
import (
"fmt"
"os"
"strings"
"git.uwaterloo.ca/csc/cscsysbot/utils/uptimerobot"
"github.com/go-chat-bot/bot"
)
func goodMorning(channel string) (string, error) {
var lines []string
lines = append(lines, fmt.Sprintf("Good morning, %s!", channel))
// Get UptimeRobot status
monitors, err := uptimerobot.GetMonitors()
if err == nil {
statuses := make(map[uptimerobot.MonitorStatus]int)
for _, mon := range monitors.Monitors {
_, ok := statuses[mon.Status]
if !ok {
statuses[mon.Status] = 1
} else {
statuses[mon.Status] += 1
}
}
var sts []string
for status, num := range statuses {
sts = append(sts, fmt.Sprintf("%d %s", num, strings.ToLower(status.String())))
}
lines = append(lines, fmt.Sprintf("Uptime Robot Monitors: %s", strings.Join(sts, ", ")))
} else {
lines = append(lines, "Unable to get Uptime Robot information")
}
return strings.Join(lines, "\n"), nil
}
func init() {
channels := strings.Split(os.Getenv("SYSCOM_CHANNELS"), ",")
if len(channels) > 0 {
config := bot.PeriodicConfig{
CronSpec: "0 0 8 * * *",
Channels: channels,
CmdFunc: goodMorning,
}
bot.RegisterPeriodicCommand("good_morning", config)
}
}

20
plugins/hello/hello.go Normal file
View File

@ -0,0 +1,20 @@
package hello
import (
"fmt"
"github.com/go-chat-bot/bot"
)
func hello(command *bot.Cmd) (msg string, err error) {
msg = fmt.Sprintf("Hello %s", command.User.Nick)
return
}
func init() {
bot.RegisterCommand(
"hello",
"Sends a 'Hello' message to you on the channel.",
"",
hello)
}

134
plugins/member/member.go Normal file
View File

@ -0,0 +1,134 @@
package member
import (
"crypto/tls"
"fmt"
"sort"
"strconv"
"strings"
"git.uwaterloo.ca/csc/cscsysbot/utils"
"github.com/go-chat-bot/bot"
"gopkg.in/ldap.v2"
)
const (
ldapServer = "ldap-master.csclub.uwaterloo.ca"
)
type ByTerm []string
func (s ByTerm) Len() int {
return len(s)
}
func (s ByTerm) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s ByTerm) Less(i, j int) bool {
a := s[i]
b := s[j]
ya, _ := strconv.ParseInt(a[1:5], 10, 32)
yb, _ := strconv.ParseInt(b[1:5], 10, 32)
if (ya == yb) {
// w, s, f (happens to be in reverse order)
return a[0] > b[0]
}
return ya < yb
}
func member(command *bot.Cmd) (string, error) {
var lines []string
if len(command.Args) != 1 {
return "An invalid number of arguments was provided. Usage is: !member userid", nil
}
authorized, _ := utils.SyscomNicks()
if (!utils.InList(authorized, command.User.Nick)) {
return "Sorry, you are not authorized to request membership information from me.", nil
}
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, 636), &tls.Config{
ServerName: ldapServer,
})
if err != nil {
return "", err
}
defer l.Close()
req := ldap.NewSearchRequest("ou=People,dc=csclub,dc=uwaterloo,dc=ca", ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(uid=%s)(objectClass=member))", command.Args[0]),
[]string{"*"}, nil)
res, err := l.Search(req)
if err != nil {
return "", err
}
if len(res.Entries) == 0 {
return fmt.Sprintf("No members matched userid %q", command.Args[0]), nil
}
entry := res.Entries[0]
lines = append(lines, fmt.Sprintf("%s (%s) -- %s",
entry.GetAttributeValue("uid"),
entry.GetAttributeValue("uidNumber"),
entry.GetAttributeValue("cn")))
lines = append(lines, entry.GetAttributeValue("program"))
positions := sort.StringSlice(entry.GetAttributeValues("position")[0:])
positions.Sort()
if len(positions) > 0 {
lines = append(lines, fmt.Sprintf("Position: %s", strings.Join(positions, ", ")))
}
// Get groups
groupReq := ldap.NewSearchRequest("ou=Group,dc=csclub,dc=uwaterloo,dc=ca", ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases, 0, 0, false,
fmt.Sprintf("(&(uniqueMember=%s)(objectClass=group))", entry.DN),
[]string{"*"}, nil)
groupRes, err := l.Search(groupReq)
if err != nil {
return "", err
}
if len(groupRes.Entries) > 0 {
groups := make([]string, len(groupRes.Entries))
for i, entry := range groupRes.Entries {
groups[i] = entry.GetAttributeValue("cn")
}
sgroups := sort.StringSlice(groups[0:])
sort.Sort(sgroups)
lines = append(lines, fmt.Sprintf("Clubs/Groups: %s", strings.Join(sgroups, ", ")))
}
terms := entry.GetAttributeValues("term")
sort.Sort(ByTerm(terms))
if len(terms) > 0 {
lines = append(lines, fmt.Sprintf("Terms: %s", strings.Join(terms, ", ")))
}
nonMemberTerms := entry.GetAttributeValues("nonMemberTerm")
sort.Sort(ByTerm(terms))
if len(nonMemberTerms) > 0 {
lines = append(lines, fmt.Sprintf("Non Member Terms: %s", strings.Join(nonMemberTerms, ", ")))
}
lines = append(lines, fmt.Sprintf("Login Shell: %s, Home Directory: %s", entry.GetAttributeValue("loginShell"), entry.GetAttributeValue("homeDirectory")))
return strings.Join(lines, "\n"), nil
}
func init() {
bot.RegisterCommand(
"member",
"Prints member information",
"userid",
member)
}

48
plugins/ping/ping.go Normal file
View File

@ -0,0 +1,48 @@
package club
import (
"fmt"
"strings"
"git.uwaterloo.ca/csc/cscsysbot/utils"
"github.com/go-chat-bot/bot"
)
type InsensitiveSlice []string
func (s InsensitiveSlice) Len() int {
return len(s)
}
func (s InsensitiveSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s InsensitiveSlice) Less(i, j int) bool {
a := strings.ToLower(s[i])
b := strings.ToLower(s[j])
return a < b
}
func ping(command *bot.Cmd) (string, error) {
nicks, _ := utils.SyscomNicks()
nicksStr := strings.Join(nicks, ", ")
if len(command.RawArgs) > 0 && len(command.RawArgs) + len(nicksStr) < 400 {
return fmt.Sprintf("%s (attn: %s)", command.RawArgs, nicksStr), nil
} else if len(command.RawArgs) > 0 {
return fmt.Sprintf("%s\n^ %s", command.RawArgs, nicksStr), nil
} else {
return fmt.Sprintf("Ping %s", nicksStr), nil
}
return "", nil
}
func init() {
bot.RegisterCommand(
"ping",
"Highlights all Syscom members.",
"Optionally a message to prepend the list of syscom members.",
ping)
}

86
plugins/tweets/tweets.go Normal file
View File

@ -0,0 +1,86 @@
package tweets
import (
"fmt"
"log"
"os"
"strings"
"git.uwaterloo.ca/csc/cscsysbot/plugins/background"
"github.com/dghubble/go-twitter/twitter"
"github.com/dghubble/oauth1"
)
func lookup(tw *twitter.Client, sn string) (*twitter.User, error) {
params := &twitter.UserLookupParams{
ScreenName: []string{sn},
}
users, _, err := tw.Users.Lookup(params)
if err != nil {
return nil, err
}
if len(users) > 0 {
return &users[0], nil
} else {
return nil, nil
}
}
func userInList(users []*twitter.User, user *twitter.User) bool {
for _, u := range users {
if u.ID == user.ID {
return true
}
}
return false
}
func init() {
// Start Twitter stream
authConfig := oauth1.NewConfig(os.Getenv("TWITTER_CONSUMER_KEY"), os.Getenv("TWITTER_CONSUMER_SECRET"))
authToken := oauth1.NewToken(os.Getenv("TWITTER_ACCESS_TOKEN"), os.Getenv("TWITTER_ACCESS_SECRET"))
httpClient := authConfig.Client(oauth1.NoContext, authToken)
tw := twitter.NewClient(httpClient)
watchScreenames := strings.Split(os.Getenv("TWITTER_USERS"), ",")
var users []*twitter.User
for _, screename := range watchScreenames {
user, err := lookup(tw, screename)
if err != nil {
log.Println("Unable to get Twitter user", screename, err)
continue
}
users = append(users, user)
}
demux := twitter.NewSwitchDemux()
demux.Tweet = func(tweet *twitter.Tweet) {
if userInList(users, tweet.User) {
background.Messages <- fmt.Sprintf("%s: %s", tweet.User.Name, strings.Replace(tweet.Text, "\n", " ", -1))
}
}
demux.StreamDisconnect = func(disconnect *twitter.StreamDisconnect) {
log.Println("Stream disconnected", disconnect)
}
userIds := make([]string, len(users))
for i, user := range users {
userIds[i] = user.IDStr
}
params := &twitter.StreamFilterParams{
Follow: userIds,
StallWarnings: twitter.Bool(true),
}
stream, err := tw.Streams.Filter(params)
if err != nil {
return
}
go demux.HandleChan(stream.Messages)
}

30
plugins/ups/ups.go Normal file
View File

@ -0,0 +1,30 @@
package ups
import (
"fmt"
"strings"
"os"
"github.com/go-chat-bot/bot"
)
func ups(command *bot.Cmd) (msg string, err error) {
upses := strings.Split(os.Getenv("UPSES"), ",")
var messages []string
for _, upsName := range upses {
ups := UPSStatus(upsName, os.Getenv("UPS_COMMUNITY_STRING"))
messages = append(messages, fmt.Sprintf("[%s] Runtime: %s, Status: %s, Battery: %s - %s\n", strings.Split(ups.Name, ".")[0], ups.EstimatedRuntime, ups.OutputStatus, ups.BatteryStatus, ups.BatteryReplaceIndicator))
}
msg = strings.Join(messages, "\n")
return
}
func init() {
bot.RegisterCommand(
"ups",
"Prints current UPS status information. (Note: this command may take some time to execute)",
"",
ups)
}

182
plugins/ups/upssnmp.go Normal file
View File

@ -0,0 +1,182 @@
package ups
import(
"fmt"
"time"
"github.com/alouca/gosnmp"
)
type BatteryStatus int
const (
BatteryStatusUnknown BatteryStatus = 1
BatteryStatusNormal BatteryStatus = 2
BatteryStatusLow BatteryStatus = 3
BatteryStatusFault BatteryStatus = 4
)
func (st BatteryStatus) String() string {
str := "Invalid"
switch(st) {
case BatteryStatusUnknown:
str = "Unknown"
case BatteryStatusNormal:
str = "Normal"
case BatteryStatusLow:
str = "Low"
case BatteryStatusFault:
str = "Fault"
}
return str
}
type BatteryReplaceIndicator int
const (
BatteryReplaceIndicatorOK BatteryReplaceIndicator = 1
BatteryReplaceIndicatorReplace BatteryReplaceIndicator = 2
)
func (st BatteryReplaceIndicator) String() string {
str := "Invalid"
switch(st) {
case BatteryReplaceIndicatorOK:
str = "Ok"
case BatteryReplaceIndicatorReplace:
str = "Replace"
}
return str
}
type OutputStatus int
const (
OutputStatusUnknown OutputStatus = 1
OutputStatusOnLine OutputStatus = 2
OutputStatusOnBattery OutputStatus = 3
OutputStatusOnSmartBoost OutputStatus = 4
OutputStatusTimedSleeping OutputStatus = 5
OutputStatusSoftwareBypass OutputStatus = 6
OutputStatusOff OutputStatus = 7
OutputStatusRebooting OutputStatus = 8
OutputStatusSwitchedBypass OutputStatus = 9
OutputStatusHardwareFailureBypass OutputStatus = 10
OutputStatusSleepingUntilPowerReturn OutputStatus = 11
OutputStatusOnSmartTrim OutputStatus = 12
)
func (st OutputStatus) String() string {
str := "Invalid"
switch(st) {
case OutputStatusUnknown:
str = "Unknown"
case OutputStatusOnLine:
str = "On Line"
case OutputStatusOnBattery:
str = "On Battery"
case OutputStatusOnSmartBoost:
str = "On Smart Boost"
case OutputStatusTimedSleeping:
str = "Timed Sleeping"
case OutputStatusSoftwareBypass:
str = "Software Bypass"
case OutputStatusOff:
str = "Off"
case OutputStatusRebooting:
str = "Rebooting"
case OutputStatusSwitchedBypass:
str = "Switched Bypass"
case OutputStatusHardwareFailureBypass:
str = "Hardware Failure Bypass"
case OutputStatusSleepingUntilPowerReturn:
str = "Sleeping Until Power Return"
case OutputStatusOnSmartTrim:
str = "On Smart Trim"
}
return str
}
func getRuntime(s *gosnmp.GoSNMP) (* time.Duration, error) {
resp, err := s.Get("1.3.6.1.4.1.318.1.1.1.2.2.3.0")
if err != nil {
return nil, err
}
if len(resp.Variables) == 0 {
return nil, nil
}
time, _ := time.ParseDuration(fmt.Sprintf("%dm", resp.Variables[0].Value.(int) / 60/ 100))
return &time, nil
}
func getBatteryStatus(s *gosnmp.GoSNMP) (* BatteryStatus, error) {
resp, err := s.Get("1.3.6.1.4.1.318.1.1.1.2.1.1.0")
if err != nil {
return nil, err
}
if len(resp.Variables) == 0 {
return nil, nil
}
status := BatteryStatus(resp.Variables[0].Value.(int))
return &status, nil
}
func getBatteryReplaceIndicator(s *gosnmp.GoSNMP) (* BatteryReplaceIndicator, error) {
resp, err := s.Get("1.3.6.1.4.1.318.1.1.1.2.2.4.0")
if err != nil {
return nil, err
}
if len(resp.Variables) == 0 {
return nil, nil
}
status := BatteryReplaceIndicator(resp.Variables[0].Value.(int))
return &status, nil
}
func getOutputStatus(s *gosnmp.GoSNMP) (* OutputStatus, error) {
resp, err := s.Get("1.3.6.1.4.1.318.1.1.1.4.1.1.0")
if err != nil {
return nil, err
}
if len(resp.Variables) == 0 {
return nil, nil
}
status := OutputStatus(resp.Variables[0].Value.(int))
return &status, nil
}
type UPS struct {
Name string
EstimatedRuntime *time.Duration
BatteryStatus *BatteryStatus
BatteryReplaceIndicator *BatteryReplaceIndicator
OutputStatus *OutputStatus
}
func UPSStatus(name string, community string) *UPS {
ups := &UPS{
Name: name,
EstimatedRuntime: nil,
BatteryStatus: nil,
BatteryReplaceIndicator: nil,
OutputStatus: nil,
}
// Get UPS status
snmp, _ := gosnmp.NewGoSNMP(name, community, gosnmp.Version1, 2)
ups.EstimatedRuntime, _ = getRuntime(snmp)
ups.BatteryStatus, _ = getBatteryStatus(snmp)
ups.BatteryReplaceIndicator, _ = getBatteryReplaceIndicator(snmp)
ups.OutputStatus, _ = getOutputStatus(snmp)
return ups
}

View File

@ -0,0 +1,45 @@
package uptimerobot
import (
"fmt"
"time"
ur "git.uwaterloo.ca/csc/cscsysbot/utils/uptimerobot"
"git.uwaterloo.ca/csc/cscsysbot/plugins/background"
)
func uptimeRobot() {
lastStatuses := make(map[int]ur.MonitorStatus)
for {
monitors, err := ur.GetMonitors()
if err != nil {
time.Sleep(time.Minute * 1)
continue
}
for _, mon := range monitors.Monitors {
lastStatus, ok := lastStatuses[mon.ID]
lastStatuses[mon.ID] = mon.Status
if !ok {
continue
}
if lastStatus != mon.Status {
if (mon.Type == ur.MonitorTypePort) {
background.Messages <- fmt.Sprintf("Uptime Robot: %s -> %s, %s:%d (%s)\n", lastStatus, mon.Status, mon.URL, mon.Type, int(mon.Port.(float64)))
} else {
background.Messages <- fmt.Sprintf("Uptime Robot: %s -> %s, %s (%s)\n", lastStatus, mon.Status, mon.URL, mon.Type)
}
}
}
time.Sleep(time.Minute * 1)
}
}
func init() {
go uptimeRobot()
}

77
plugins/url/url.go Normal file
View File

@ -0,0 +1,77 @@
// Based on https://github.com/go-chat-bot/plugins/blob/master/url/url.go
// Copyright (c) 2014 Fábio Gomes
// The MIT License (MIT)
package url
import (
"github.com/go-chat-bot/bot"
"github.com/go-chat-bot/plugins/web"
"html"
"net/url"
"regexp"
"strings"
"fmt"
)
const (
minDomainLength = 3
)
var (
re = regexp.MustCompile("<title>\\n*?(.*?)\\n*?<\\/title>")
)
func canBeURLWithoutProtocol(text string) bool {
return len(text) > minDomainLength &&
!strings.HasPrefix(text, "http") &&
strings.Contains(text, ".")
}
func extractURL(text string) string {
extractedURL := ""
for _, value := range strings.Split(text, " ") {
if canBeURLWithoutProtocol(value) {
value = "http://" + value
}
parsedURL, err := url.Parse(value)
if err != nil {
continue
}
if strings.HasPrefix(parsedURL.Scheme, "http") {
extractedURL = parsedURL.String()
break
}
}
return extractedURL
}
func urlTitle(cmd *bot.PassiveCmd) (string, error) {
URL := extractURL(cmd.Raw)
if URL == "" {
return "", nil
}
body, err := web.GetBody(URL)
if err != nil {
return "", err
}
title := re.FindString(string(body))
if title == "" {
return "", nil
}
title = strings.Replace(title, "\n", "", -1)
title = title[strings.Index(title, ">")+1 : strings.LastIndex(title, "<")]
return fmt.Sprintf("%q", html.UnescapeString(title)), nil
}
func init() {
bot.RegisterPassiveCommand(
"url",
urlTitle)
}

11
utils/lists.go Normal file
View File

@ -0,0 +1,11 @@
package utils
func InList(l []string, s string) bool {
for _, i := range l {
if i == s {
return true
}
}
return false
}

77
utils/syscom.go Normal file
View File

@ -0,0 +1,77 @@
package utils
import(
"crypto/tls"
"fmt"
"strings"
"sort"
"gopkg.in/ldap.v2"
)
type InsensitiveSlice []string
func (s InsensitiveSlice) Len() int {
return len(s)
}
func (s InsensitiveSlice) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s InsensitiveSlice) Less(i, j int) bool {
a := strings.ToLower(s[i])
b := strings.ToLower(s[j])
return a < b
}
const (
ldapServer = "ldap-master.csclub.uwaterloo.ca"
)
func SyscomNicks() ([]string, error) {
l, err := ldap.DialTLS("tcp", fmt.Sprintf("%s:%d", ldapServer, 636), &tls.Config{
ServerName: ldapServer,
})
if err != nil {
return make([]string, 0), err
}
defer l.Close()
// Get members
req := ldap.NewSearchRequest("ou=Group,dc=csclub,dc=uwaterloo,dc=ca", ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases, 0, 0, false,
"(&(cn=syscom)(objectClass=posixGroup))",
[]string{"*"}, nil)
res, err := l.Search(req)
if err != nil {
return make([]string, 0), err
}
if len(res.Entries) > 0 {
entry := res.Entries[0]
members := entry.GetAttributeValues("uniqueMember")
memberUids := make([]string, len(members))
for i, member := range members {
memberUids[i] = strings.Replace(strings.Replace(member, "uid=", "", -1),
",ou=People,dc=csclub,dc=uwaterloo,dc=ca", "", -1)
// Exceptions: some use different nicks
switch memberUids[i] {
case "ztseguin":
memberUids[i] = "zseguin"
case "nablack":
memberUids[i] = "Na"
case "laden":
memberUids[i] = "Luqman"
}
}
smemberUids := InsensitiveSlice(memberUids[0:])
sort.Sort(smemberUids)
return []string(smemberUids), nil
}
return make([]string, 0), nil
}

View File

@ -0,0 +1,120 @@
package uptimerobot
import (
"fmt"
"net/http"
"strings"
"io/ioutil"
"encoding/json"
"time"
"net"
"os"
)
// Really basic, no pagination support
type Monitors struct {
Monitors []Monitor `json:"monitors"`
}
type Monitor struct {
ID int `json:"id"`
FriendlyName string `json:"friendly_name"`
URL string `json:"url"`
Type MonitorType `json:"type"`
Status MonitorStatus `json:"status"`
// Type specifc properties
Port interface{} `json:"port"`
}
type MonitorType int
const (
_ MonitorType = iota
MonitorTypeHTTP
MonitorTypeKeyword
MonitorTypePing
MonitorTypePort
)
func (t MonitorType) String() string {
switch t {
case MonitorTypeHTTP:
return "HTTP"
case MonitorTypeKeyword:
return "HTTP Keyword"
case MonitorTypePing:
return "Ping"
case MonitorTypePort:
return "Port"
}
return "Invalid"
}
type MonitorStatus int
const (
MonitorStatusPaused MonitorStatus = 0
MonitorStatusNotCheckedYet = 1
MonitorStatusUp = 2
MonitorStatusSeemsDown = 8
MonitorStatusDown = 9
)
func (s MonitorStatus) String() string {
switch s {
case MonitorStatusPaused:
return "Paused"
case MonitorStatusNotCheckedYet:
return "Not Checked Yet"
case MonitorStatusUp:
return "Up"
case MonitorStatusSeemsDown:
return "Seems Down"
case MonitorStatusDown:
return "Down"
}
return "Invalid"
}
func GetMonitors() (Monitors, error) {
var monitors Monitors
transport := &http.Transport{
Dial: (&net.Dialer{
Timeout: time.Second * 10,
}).Dial,
TLSHandshakeTimeout: time.Second * 10,
}
net := http.Client{
Timeout: time.Second * 10,
Transport: transport,
}
url := "https://api.uptimerobot.com/v2/getMonitors"
payload := strings.NewReader(fmt.Sprintf("api_key=%s&format=json", os.Getenv("UPTIME_ROBOT_API_KEY")))
req, err := http.NewRequest("POST", url, payload)
if err != nil {
return monitors, err
}
req.Header.Add("content-type", "application/x-www-form-urlencoded")
req.Header.Add("cache-control", "no-cache")
res, err := net.Do(req)
if err != nil {
return monitors, err
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
return monitors, err
}
err = json.Unmarshal(body, &monitors)
if err != nil {
return monitors, err
}
return monitors, nil
}