<commit_before>package main

import "flag"
import "fmt"
import log "github.com/Sirupsen/logrus"
import "strconv"
import "time"
import "net"
import "os"
import "encoding/json"

//import "github.com/Shopify/sarama"
import "github.com/google/gopacket"
import "github.com/google/gopacket/pcap"
import "github.com/google/gopacket/layers"

/*
Plans:

    code cleanup (e.g. break up handlePacket, switch everything to camelCase)
    stats output
    perf testing
    TCP flow support
release v2

    syslog logging
    logging to kafka
    add PF_RING support
release v3

    maybe use something with a larger keyspace than the query ID for the conntable map
    maybe not so many string conversions?
    add more Types to gopacket
*/

/*

DNS log entry struct and helper functions

*/
type dnsLogEntry struct {
	Query_ID      uint16 `json:"query_id"`
	Response_Code int    `json:"response_code"`
	Question      string `json:"question"`
	Question_Type string `json:"question_type"`
	Answer        string `json:"answer"`
	Answer_Type   string `json:"answer_type"`
	TTL           uint32 `json:"ttl"`
	Server        net.IP `json:"server"`
	Client        net.IP `json:"client"`
	Timestamp     string `json:"timestamp"`

	encoded []byte //to hold the marshaled data structure
	err     error  //encoding errors
}

func (dle *dnsLogEntry) ensureEncoded() {
	if dle.encoded == nil && dle.err == nil {
		dle.encoded, dle.err = json.Marshal(dle)
	}
}

func (dle *dnsLogEntry) Length() int {
	dle.ensureEncoded()
	return len(dle.encoded)
}

func (dle *dnsLogEntry) Encode() ([]byte, error) {
	dle.ensureEncoded()
	return dle.encoded, dle.err
}

type dnsMapEntry struct {
	entry    *layers.DNS
	inserted time.Time
}

//background task to clear out stale entries in the conntable
//one of these gets spun up for every packet handling thread
func cleanDnsCache(conntable *map[uint16]dnsMapEntry, max_age time.Duration, interval time.Duration) {

	for {
		time.Sleep(interval)

		//max_age should be negative, e.g. -1m
		cleanup_cutoff := time.Now().Add(max_age)
		for key, item := range *conntable {
			if item.inserted.Before(cleanup_cutoff) {
				log.Debug("conntable GC: cleanup query ID " + strconv.Itoa(int(key)))
				delete(*conntable, key)
			}
		}
	}
}

/*
    The gopacket DNS layer doesn't have a lot of good String()
    conversion methods, so we have to do a lot of that ourselves
    here.  Much of this should move back into gopacket.  Also a
    little worried about the perf impact of doing string conversions
    in this thread...
*/
func TypeString(dnsType layers.DNSType) string {
    switch dnsType {
        default:
        	//take a blind stab...at least this shouldn't *lose* data
        	return strconv.Itoa(int(dnsType))
        case layers.DNSTypeA:
        	return "A"
        case layers.DNSTypeAAAA:
        	return "AAAA"
        case layers.DNSTypeCNAME:
        	return "CNAME"
        case layers.DNSTypeMX:
        	return "MX"
        case layers.DNSTypeNS:
        	return "NS"
        case layers.DNSTypePTR:
        	return "PTR"
        case layers.DNSTypeTXT:
        	return "TXT"
        case layers.DNSTypeSOA:
        	return "SOA"
        case layers.DNSTypeSRV:
        	return "SRV"
        case 255: //ANY query per http://tools.ietf.org/html/rfc1035#page-12
		    return "ANY"
        }
}

/*
    The gopacket DNS layer doesn't have a lot of good String()
    conversion methods, so we have to do a lot of that ourselves
    here.  Much of this should move back into gopacket.  Also a
    little worried about the perf impact of doing string conversions
    in this thread...
*/
func RrString(rr layers.DNSResourceRecord) string {
    switch rr.Type {
        default:
        	//take a blind stab...at least this shouldn't *lose* data
        	return string(rr.Data)
        case layers.DNSTypeA:
        	return rr.IP.String()
        case layers.DNSTypeAAAA:
        	return rr.IP.String()
        case layers.DNSTypeCNAME:
        	return string(rr.CNAME)
        case layers.DNSTypeMX:
        	//TODO: add the priority
        	return string(rr.MX.Name)
        case layers.DNSTypeNS:
        	return string(rr.NS)
        case layers.DNSTypePTR:
        	return string(rr.PTR)
        case layers.DNSTypeTXT:
        	return string(rr.TXT)
        case layers.DNSTypeSOA:
        	//TODO: rebuild the full SOA string
        	return string(rr.SOA.RName)
        case layers.DNSTypeSRV:
        	//TODO: rebuild the full SRV string
        	return string(rr.SRV.Name)
        }
}

/* validate if DNS, make conntable entry and output
   to log channel if there is a match
*/
func handlePacket(packets chan gopacket.Packet, logC chan dnsLogEntry,
	gc_interval time.Duration, gc_age time.Duration) {

	//DNS IDs are stored as uint16s by the gopacket DNS layer
	//TODO: fix the memory leak of failed lookups by making this a ttlcache
	var conntable = make(map[uint16]dnsMapEntry)

	//setup garbage collection for this map
	go cleanDnsCache(&conntable, gc_age, gc_interval)

	for packet := range packets {
		//TODO: there must be a better way of doing this with gopacket
		var srcIP net.IP
		var dstIP net.IP

		if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {
			ipData, _ := ipLayer.(*layers.IPv4)
			srcIP = ipData.SrcIP
			dstIP = ipData.DstIP
		} else if ipLayer := packet.Layer(layers.LayerTypeIPv6); ipLayer != nil {
			ipData, _ := ipLayer.(*layers.IPv6)
			srcIP = ipData.SrcIP
			dstIP = ipData.DstIP
		} else {
			//non-IP transport?  Ignore this packet
			log.Debug("Got non-IP packet: " + packet.String())
			continue
		}

		if dnsLayer := packet.Layer(layers.LayerTypeDNS); dnsLayer != nil {
			// Get actual DNS data from this layer
			dns, _ := dnsLayer.(*layers.DNS)

			//skip non-query stuff (Updates, AXFRs, etc)
			if dns.OpCode != layers.DNSOpCodeQuery {
				log.Debug("Saw non-update DNS packet: " + packet.String())
				continue
			}

			//this is a Query Response packet
			if dns.QR == true {
				if item, ok := conntable[dns.ID]; ok != false {
					question := item.entry
					//We have both legs of the connection, so drop the connection from the table
					log.Debug("Got 'answer' leg of query ID: " + strconv.Itoa(int(question.ID)))
					delete(conntable, question.ID)

					/*
					   http://forums.devshed.com/dns-36/dns-packet-question-section-1-a-183026.html
					   multiple questions isn't really a thing, so we'll loop over the answers and
					   insert the question section from the original query.  This means a successful
					   ANY query may result in a lot of seperate log entries.  The query ID will be
					   the same on all of those entries, however, so you can rebuild the query that
					   way.

					   TODO: Also loop through Additional records in addition to Answers
					*/

                    questionType := TypeString(question.Questions[0].Type)

					//a response code other than 0 means failure of some kind
					if dns.ResponseCode != 0 {

						logEntry := dnsLogEntry{
							Query_ID:      dns.ID,
							Question:      string(question.Questions[0].Name),
							Response_Code: int(dns.ResponseCode),
							Question_Type: questionType,
							Answer:        dns.ResponseCode.String(),
							Answer_Type:   "",
							TTL:           0,
							//this is the answer packet, which comes from the server...
							Server: srcIP,
							//...and goes to the client
							Client:    dstIP,
							Timestamp: time.Now().UTC().Format(time.RFC3339),
						}

						logC <- logEntry

						continue
					}

					for _, answer := range dns.Answers {

                        answerString := RrString(answer)
                        typeString := TypeString(answer.Type)

						logEntry := dnsLogEntry{
							Query_ID:      dns.ID,
							Question:      string(question.Questions[0].Name),
							Response_Code: int(dns.ResponseCode),
							Question_Type: questionType,
							Answer:        answerString,
							Answer_Type:   typeString,
							TTL:           answer.TTL,
							//this is the answer packet, which comes from the server...
							Server: srcIP,
							//...and goes to the client
							Client:    dstIP,
							Timestamp: time.Now().UTC().Format(time.RFC3339),
						}

						logC <- logEntry

					}
				} else {
					//This might happen if we get a query ID collision
					log.Debug("Got a Query Response and can't find a query for ID " + strconv.Itoa(int(dns.ID)))
					continue
				}
			} else {
				//This is the initial query.  save it for later.
				log.Debug("Got the 'question' leg of query ID " + strconv.Itoa(int(dns.ID)))
				mapEntry := dnsMapEntry{
					entry:    dns,
					inserted: time.Now(),
				}
				conntable[dns.ID] = mapEntry
			}
		}
	}
}

//Round-robin log messages to log sinks
func logConn(logC chan dnsLogEntry, stdout bool, file bool, kafka bool,
	filename string, kafka_brokers string, kafka_topic string) {

	var logs []chan dnsLogEntry

	if stdout {
		log.Debug("STDOUT logging enabled")
		stdoutChan := make(chan dnsLogEntry)
		logs = append(logs, stdoutChan)
		go logConnStdout(stdoutChan)
	}

	if file {
		log.Debug("file logging enabled to " + filename)
		fileChan := make(chan dnsLogEntry)
		logs = append(logs, fileChan)
		go logConnFile(fileChan, filename)
	}

	if kafka && false {
		log.Debug("kafka logging enabled")
		kafkaChan := make(chan dnsLogEntry)
		logs = append(logs, kafkaChan)
		go logConnKafka(kafkaChan, kafka_brokers, kafka_topic)
	}

	//setup is done, now we sit here and dispatch messages to the configured sinks
	for message := range logC {
		for _, logChan := range logs {
			logChan <- message
		}
	}
}

func logConnStdout(logC chan dnsLogEntry) {
	for message := range logC {
		encoded, _ := message.Encode()
		fmt.Println(string(encoded))
	}
}

func logConnFile(logC chan dnsLogEntry, filename string) {

	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Debug("could not open logfile for writing!")
		panic(err)
	}

	defer f.Close()

	for message := range logC {
		encoded, _ := message.Encode()
		f.WriteString(string(encoded) + "\n")
	}
}

func logConnKafka(logC chan dnsLogEntry, kafka_brokers string, kafka_topic string) {
	for message := range logC {
		//marshal to JSON.  Maybe we should do this in the log thread?
		encoded, _ := message.Encode()
		fmt.Println("Kafka: " + string(encoded))
	}
}

func main() {

	var dev = flag.String("dev", "", "Capture Device")
	var kafka_brokers = flag.String("kafka_brokers", os.Getenv("KAFKA_PEERS"), "The Kafka brokers to connect to, as a comma separated list")
	var kafka_topic = flag.String("kafka_topic", "", "Kafka topic for output")
	var bpf = flag.String("bpf", "port 53", "BPF Filter")
	var pcapFile = flag.String("pcap", "", "pcap file")
	var logfile = flag.String("logfile", "", "log file (recommended for debug only")
	var quiet = flag.Bool("quiet", false, "do not log to stdout")
	var gc_age = flag.String("gc_age", "-1m", "How old a connection table entry should be before it is garbage collected.")
	var gc_interval = flag.String("gc_interval", "3m", "How often to run garbage collection.")
	var debug = flag.Bool("debug", false, "Enable debug logging")

	flag.Parse()

	var handle *pcap.Handle
	var err error

	if *dev != "" {
		handle, err = pcap.OpenLive(*dev, 65536, true, pcap.BlockForever)
		if err != nil {
			log.Fatal(err)
		}
	} else if *pcapFile != "" {
		handle, err = pcap.OpenOffline(*pcapFile)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Fatal("You must specify either a capture device or a pcap file")
	}

	defer handle.Close()

	err = handle.SetBPFFilter(*bpf)
	if err != nil {
		log.Fatal(err)
	}

	if *debug {
		log.SetLevel(log.DebugLevel)
	}

	/* spin up logging thread */
	var logChan = make(chan dnsLogEntry)
	var log_file bool = false
	var log_kafka bool = false

	if *logfile != "" {
		log_file = true
	}

	if *kafka_brokers != "" && *kafka_topic != "" {
		log_kafka = true
	}

	var gc_age_dur time.Duration
	var gc_interval_dur time.Duration

	if gc_age_tmp, err := time.ParseDuration(*gc_age); err != nil {
		log.Fatal("Your gc_age parameter was not parseable.  Use a string like '-1m'")
	} else {
		gc_age_dur = gc_age_tmp
	}

	if gc_interval_tmp, err := time.ParseDuration(*gc_interval); err != nil {
		log.Fatal("Your gc_age parameter was not parseable.  Use a string like '3m'")
	} else {
		gc_interval_dur = gc_interval_tmp
	}

	go logConn(logChan, !*quiet, log_file, log_kafka, *logfile, *kafka_brokers, *kafka_topic)

	/* init channels for the packet handlers and kick off handler threads */
	var channels [8]chan gopacket.Packet
	for i := 0; i < 8; i++ {
		channels[i] = make(chan gopacket.Packet)
		go handlePacket(channels[i], logChan, gc_interval_dur, gc_age_dur)
	}

	// Use the handle as a packet source to process all packets
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		// Dispatch packets here
		if net := packet.NetworkLayer(); net != nil {
			/*  load balance the processiing over 8 threads
			    FashHash is consistant for A->B and B->A hashes, which simplifies
			    our connection tracking problem a bit by letting us keep
			    per-worker connection pools instead of a global pool.
			*/
			channels[int(net.NetworkFlow().FastHash())&0x7] <- packet
		}
	}
}
<commit_msg>re-factor some of the handlePacket code to nest less deeply<commit_after>package main

import "flag"
import "fmt"
import log "github.com/Sirupsen/logrus"
import "strconv"
import "time"
import "net"
import "os"
import "encoding/json"

//import "github.com/Shopify/sarama"
import "github.com/google/gopacket"
import "github.com/google/gopacket/pcap"
import "github.com/google/gopacket/layers"

/*
Plans:

    code cleanup (e.g. break up handlePacket, switch everything to camelCase)
    stats output
    perf testing
    TCP flow support
release v2

    syslog logging
    logging to kafka
    add PF_RING support
release v3

    maybe use something with a larger keyspace than the query ID for the conntable map
    maybe not so many string conversions?
    add more Types to gopacket
*/

/*

DNS log entry struct and helper functions

*/
type dnsLogEntry struct {
	Query_ID      uint16 `json:"query_id"`
	Response_Code int    `json:"response_code"`
	Question      string `json:"question"`
	Question_Type string `json:"question_type"`
	Answer        string `json:"answer"`
	Answer_Type   string `json:"answer_type"`
	TTL           uint32 `json:"ttl"`
	Server        net.IP `json:"server"`
	Client        net.IP `json:"client"`
	Timestamp     string `json:"timestamp"`

	encoded []byte //to hold the marshaled data structure
	err     error  //encoding errors
}

func (dle *dnsLogEntry) ensureEncoded() {
	if dle.encoded == nil && dle.err == nil {
		dle.encoded, dle.err = json.Marshal(dle)
	}
}

func (dle *dnsLogEntry) Length() int {
	dle.ensureEncoded()
	return len(dle.encoded)
}

func (dle *dnsLogEntry) Encode() ([]byte, error) {
	dle.ensureEncoded()
	return dle.encoded, dle.err
}

type dnsMapEntry struct {
	entry    *layers.DNS
	inserted time.Time
}

//background task to clear out stale entries in the conntable
//one of these gets spun up for every packet handling thread
func cleanDnsCache(conntable *map[uint16]dnsMapEntry, max_age time.Duration, interval time.Duration) {

	for {
		time.Sleep(interval)

		//max_age should be negative, e.g. -1m
		cleanup_cutoff := time.Now().Add(max_age)
		for key, item := range *conntable {
			if item.inserted.Before(cleanup_cutoff) {
				log.Debug("conntable GC: cleanup query ID " + strconv.Itoa(int(key)))
				delete(*conntable, key)
			}
		}
	}
}

/*
    The gopacket DNS layer doesn't have a lot of good String()
    conversion methods, so we have to do a lot of that ourselves
    here.  Much of this should move back into gopacket.  Also a
    little worried about the perf impact of doing string conversions
    in this thread...
*/
func TypeString(dnsType layers.DNSType) string {
    switch dnsType {
        default:
        	//take a blind stab...at least this shouldn't *lose* data
        	return strconv.Itoa(int(dnsType))
        case layers.DNSTypeA:
        	return "A"
        case layers.DNSTypeAAAA:
        	return "AAAA"
        case layers.DNSTypeCNAME:
        	return "CNAME"
        case layers.DNSTypeMX:
        	return "MX"
        case layers.DNSTypeNS:
        	return "NS"
        case layers.DNSTypePTR:
        	return "PTR"
        case layers.DNSTypeTXT:
        	return "TXT"
        case layers.DNSTypeSOA:
        	return "SOA"
        case layers.DNSTypeSRV:
        	return "SRV"
        case 255: //ANY query per http://tools.ietf.org/html/rfc1035#page-12
		    return "ANY"
        }
}

/*
    The gopacket DNS layer doesn't have a lot of good String()
    conversion methods, so we have to do a lot of that ourselves
    here.  Much of this should move back into gopacket.  Also a
    little worried about the perf impact of doing string conversions
    in this thread...
*/
func RrString(rr layers.DNSResourceRecord) string {
    switch rr.Type {
        default:
        	//take a blind stab...at least this shouldn't *lose* data
        	return string(rr.Data)
        case layers.DNSTypeA:
        	return rr.IP.String()
        case layers.DNSTypeAAAA:
        	return rr.IP.String()
        case layers.DNSTypeCNAME:
        	return string(rr.CNAME)
        case layers.DNSTypeMX:
        	//TODO: add the priority
        	return string(rr.MX.Name)
        case layers.DNSTypeNS:
        	return string(rr.NS)
        case layers.DNSTypePTR:
        	return string(rr.PTR)
        case layers.DNSTypeTXT:
        	return string(rr.TXT)
        case layers.DNSTypeSOA:
        	//TODO: rebuild the full SOA string
        	return string(rr.SOA.RName)
        case layers.DNSTypeSRV:
        	//TODO: rebuild the full SRV string
        	return string(rr.SRV.Name)
        }
}

/* validate if DNS, make conntable entry and output
   to log channel if there is a match
*/
func handlePacket(packets chan gopacket.Packet, logC chan dnsLogEntry,
	gc_interval time.Duration, gc_age time.Duration) {

	//DNS IDs are stored as uint16s by the gopacket DNS layer
	//TODO: fix the memory leak of failed lookups by making this a ttlcache
	var conntable = make(map[uint16]dnsMapEntry)

	//setup garbage collection for this map
	go cleanDnsCache(&conntable, gc_age, gc_interval)

	for packet := range packets {
		//TODO: there must be a better way of doing this with gopacket
		var srcIP net.IP
		var dstIP net.IP

		if ipLayer := packet.Layer(layers.LayerTypeIPv4); ipLayer != nil {
			ipData, _ := ipLayer.(*layers.IPv4)
			srcIP = ipData.SrcIP
			dstIP = ipData.DstIP
		} else if ipLayer := packet.Layer(layers.LayerTypeIPv6); ipLayer != nil {
			ipData, _ := ipLayer.(*layers.IPv6)
			srcIP = ipData.SrcIP
			dstIP = ipData.DstIP
		} else {
			//non-IP transport?  Ignore this packet
			log.Debug("Got non-IP packet: " + packet.String())
			continue
		}

		if dnsLayer := packet.Layer(layers.LayerTypeDNS); dnsLayer != nil {
			// Get actual DNS data from this layer
			dns, _ := dnsLayer.(*layers.DNS)

			//skip non-query stuff (Updates, AXFRs, etc)
			if dns.OpCode != layers.DNSOpCodeQuery {
				log.Debug("Saw non-update DNS packet: " + packet.String())
				continue
			}

            item, found_item := conntable[dns.ID];

			//this is a Query Response packet
			if dns.QR && found_item {
				question := item.entry
				//We have both legs of the connection, so drop the connection from the table
				log.Debug("Got 'answer' leg of query ID: " + strconv.Itoa(int(question.ID)))
				delete(conntable, question.ID)

				/*
				   http://forums.devshed.com/dns-36/dns-packet-question-section-1-a-183026.html
				   multiple questions isn't really a thing, so we'll loop over the answers and
				   insert the question section from the original query.  This means a successful
				   ANY query may result in a lot of seperate log entries.  The query ID will be
				   the same on all of those entries, however, so you can rebuild the query that
				   way.

				   TODO: Also loop through Additional records in addition to Answers
				*/

                questionType := TypeString(question.Questions[0].Type)

				//a response code other than 0 means failure of some kind
				if dns.ResponseCode != 0 {

					logEntry := dnsLogEntry{
						Query_ID:      dns.ID,
						Question:      string(question.Questions[0].Name),
						Response_Code: int(dns.ResponseCode),
						Question_Type: questionType,
						Answer:        dns.ResponseCode.String(),
						Answer_Type:   "",
						TTL:           0,
						//this is the answer packet, which comes from the server...
						Server: srcIP,
						//...and goes to the client
						Client:    dstIP,
						Timestamp: time.Now().UTC().Format(time.RFC3339),
					}

					logC <- logEntry

					continue
				}

				for _, answer := range dns.Answers {

                    answerString := RrString(answer)
                    typeString := TypeString(answer.Type)

					logEntry := dnsLogEntry{
						Query_ID:      dns.ID,
						Question:      string(question.Questions[0].Name),
						Response_Code: int(dns.ResponseCode),
						Question_Type: questionType,
						Answer:        answerString,
						Answer_Type:   typeString,
						TTL:           answer.TTL,
						//this is the answer packet, which comes from the server...
						Server: srcIP,
						//...and goes to the client
						Client:    dstIP,
						Timestamp: time.Now().UTC().Format(time.RFC3339),
					}

					logC <- logEntry
				}

			} else if dns.QR && !found_item {
				//This might happen if we get a query ID collision
				log.Debug("Got a Query Response and can't find a query for ID " + strconv.Itoa(int(dns.ID)))
				continue
			} else {
				//This is the initial query.  save it for later.
				log.Debug("Got the 'question' leg of query ID " + strconv.Itoa(int(dns.ID)))
				mapEntry := dnsMapEntry{
					entry:    dns,
					inserted: time.Now(),
				}
				conntable[dns.ID] = mapEntry
			}
		}
	}
}

//Round-robin log messages to log sinks
func logConn(logC chan dnsLogEntry, stdout bool, file bool, kafka bool,
	filename string, kafka_brokers string, kafka_topic string) {

	var logs []chan dnsLogEntry

	if stdout {
		log.Debug("STDOUT logging enabled")
		stdoutChan := make(chan dnsLogEntry)
		logs = append(logs, stdoutChan)
		go logConnStdout(stdoutChan)
	}

	if file {
		log.Debug("file logging enabled to " + filename)
		fileChan := make(chan dnsLogEntry)
		logs = append(logs, fileChan)
		go logConnFile(fileChan, filename)
	}

	if kafka && false {
		log.Debug("kafka logging enabled")
		kafkaChan := make(chan dnsLogEntry)
		logs = append(logs, kafkaChan)
		go logConnKafka(kafkaChan, kafka_brokers, kafka_topic)
	}

	//setup is done, now we sit here and dispatch messages to the configured sinks
	for message := range logC {
		for _, logChan := range logs {
			logChan <- message
		}
	}
}

func logConnStdout(logC chan dnsLogEntry) {
	for message := range logC {
		encoded, _ := message.Encode()
		fmt.Println(string(encoded))
	}
}

func logConnFile(logC chan dnsLogEntry, filename string) {

	f, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0666)
	if err != nil {
		log.Debug("could not open logfile for writing!")
		panic(err)
	}

	defer f.Close()

	for message := range logC {
		encoded, _ := message.Encode()
		f.WriteString(string(encoded) + "\n")
	}
}

func logConnKafka(logC chan dnsLogEntry, kafka_brokers string, kafka_topic string) {
	for message := range logC {
		//marshal to JSON.  Maybe we should do this in the log thread?
		encoded, _ := message.Encode()
		fmt.Println("Kafka: " + string(encoded))
	}
}

func main() {

	var dev = flag.String("dev", "", "Capture Device")
	var kafka_brokers = flag.String("kafka_brokers", os.Getenv("KAFKA_PEERS"), "The Kafka brokers to connect to, as a comma separated list")
	var kafka_topic = flag.String("kafka_topic", "", "Kafka topic for output")
	var bpf = flag.String("bpf", "port 53", "BPF Filter")
	var pcapFile = flag.String("pcap", "", "pcap file")
	var logfile = flag.String("logfile", "", "log file (recommended for debug only")
	var quiet = flag.Bool("quiet", false, "do not log to stdout")
	var gc_age = flag.String("gc_age", "-1m", "How old a connection table entry should be before it is garbage collected.")
	var gc_interval = flag.String("gc_interval", "3m", "How often to run garbage collection.")
	var debug = flag.Bool("debug", false, "Enable debug logging")

	flag.Parse()

	var handle *pcap.Handle
	var err error

	if *dev != "" {
		handle, err = pcap.OpenLive(*dev, 65536, true, pcap.BlockForever)
		if err != nil {
			log.Fatal(err)
		}
	} else if *pcapFile != "" {
		handle, err = pcap.OpenOffline(*pcapFile)
		if err != nil {
			log.Fatal(err)
		}
	} else {
		log.Fatal("You must specify either a capture device or a pcap file")
	}

	defer handle.Close()

	err = handle.SetBPFFilter(*bpf)
	if err != nil {
		log.Fatal(err)
	}

	if *debug {
		log.SetLevel(log.DebugLevel)
	}

	/* spin up logging thread */
	var logChan = make(chan dnsLogEntry)
	var log_file bool = false
	var log_kafka bool = false

	if *logfile != "" {
		log_file = true
	}

	if *kafka_brokers != "" && *kafka_topic != "" {
		log_kafka = true
	}

	var gc_age_dur time.Duration
	var gc_interval_dur time.Duration

	if gc_age_tmp, err := time.ParseDuration(*gc_age); err != nil {
		log.Fatal("Your gc_age parameter was not parseable.  Use a string like '-1m'")
	} else {
		gc_age_dur = gc_age_tmp
	}

	if gc_interval_tmp, err := time.ParseDuration(*gc_interval); err != nil {
		log.Fatal("Your gc_age parameter was not parseable.  Use a string like '3m'")
	} else {
		gc_interval_dur = gc_interval_tmp
	}

	go logConn(logChan, !*quiet, log_file, log_kafka, *logfile, *kafka_brokers, *kafka_topic)

	/* init channels for the packet handlers and kick off handler threads */
	var channels [8]chan gopacket.Packet
	for i := 0; i < 8; i++ {
		channels[i] = make(chan gopacket.Packet)
		go handlePacket(channels[i], logChan, gc_interval_dur, gc_age_dur)
	}

	// Use the handle as a packet source to process all packets
	packetSource := gopacket.NewPacketSource(handle, handle.LinkType())
	for packet := range packetSource.Packets() {
		// Dispatch packets here
		if net := packet.NetworkLayer(); net != nil {
			/*  load balance the processiing over 8 threads
			    FashHash is consistant for A->B and B->A hashes, which simplifies
			    our connection tracking problem a bit by letting us keep
			    per-worker connection pools instead of a global pool.
			*/
			channels[int(net.NetworkFlow().FastHash())&0x7] <- packet
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"io/ioutil"
	"encoding/json"
	"strings"
	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/go-redis/redis"
	"strconv"
	"math"
	// "time"
)

type device struct {
	// Gps_num float32 `json:"gps_num"`
	// App string `json:"app"`
	// Gps_alt float32 `json:"gps_alt"`
	// Fmt_opt int `json:"fmt_opt"`
	// Device string `json:"device"`
	// S_d2 float32 `json:"s_d2"`
	S_d0 float32 `json:"s_d0"`
	// S_d1 float32 `json:"s_d1"`
	S_h0 float32 `json:"s_h0"`
	SiteName string `json:"SiteName"`
	// Gps_fix float32 `json:"gps_fix"`
	// Ver_app string `json:"ver_app"`
	Gps_lat float64 `json:"gps_lat"`
	S_t0 float32 `json:"s_t0"`
	Timestamp string `json:"timestamp"`
	Gps_lon float64 `json:"gps_lon"`
	// Date string `json:"date"`
	// Tick float32 `json:"tick"`
	Device_id string `json:"device_id"`
	// S_1 float32 `json:"s_1"`
	// S_0 float32 `json:"s_0"`
	// S_3 float32 `json:"s_3"`
	// S_2 float32 `json:"s_2"`
	// Ver_format string `json:"ver_format"`
	// Time string `json:"time"`
}

type airbox struct {
	Source string `json:"source"`
	Feeds []device `json:"feeds"`
	Version string `json:"version"`
	Num_of_records int `json:"num_of_records"`
}

type subscribeid struct {
	Device_id []string `json:"device_id"`
	Sitename []string `json:"sitename"`
}

var bot *linebot.Client
var airbox_json airbox
var lass_json airbox
var maps_json airbox
var all_device []device
var history_json subscribeid
var	client=redis.NewClient(&redis.Options{
		Addr:"hipposerver.ddns.net:6379",
		Password:"",
		DB:0,
	})

func main() {
	url := "https://data.lass-net.org/data/last-all-airbox.json"
	req, _ := http.NewRequest("GET", url, nil)
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	errs := json.Unmarshal(body, &airbox_json)
	if errs != nil {
		fmt.Println(errs)
	}

	url = "https://data.lass-net.org/data/last-all-lass.json"
	req, _ = http.NewRequest("GET", url, nil)
	res, _ = http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	errs = json.Unmarshal(body, &lass_json)
	if errs != nil {
		fmt.Println(errs)
	}

	url = "https://data.lass-net.org/data/last-all-maps.json"
	req, _ = http.NewRequest("GET", url, nil)
	res, _ = http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	errs = json.Unmarshal(body, &maps_json)
	if errs != nil {
		fmt.Println(errs)
	}

	all_device=append(maps_json.Feeds,lass_json.Feeds...)
	all_device=append(all_device,airbox_json.Feeds...)

	url = "https://data.lass-net.org/data/airbox_list.json"
	req, _ = http.NewRequest("GET", url, nil)
	res, _ = http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	errs = json.Unmarshal(body, &history_json)
	if errs != nil {
		fmt.Println(errs)
	}
	// pushmessage()
	// fmt.Println(airbox_json)
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	// _,_=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)

}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				var txtmessage string
				inText := strings.ToLower(message.Text)
				if strings.Contains(inText,"訂閱"){
					userID:=event.Source.UserID
					// pong, _ := client.Ping().Result()
					// txtmessage=pong
					for i:=0; i<len(history_json.Device_id); i++ {
						if strings.Contains(inText,strings.ToLower(history_json.Device_id[i]))||strings.Contains(inText,history_json.Sitename[i]) {
							val, err:=client.Get(history_json.Device_id[i]).Result()
							if err!=nil{
								if strings.Contains(inText,"取消"){
									txtmessage="你並沒有訂閱此ID。"
									break
								}
								client.Set(history_json.Device_id[i],userID,0)
								txtmessage="訂閱成功!"
								break
							}
							if strings.Contains(inText,"取消"){
								stringSlice:=strings.Split(val,",")
								if stringInSlice(userID,stringSlice){
									if len(stringSlice)==1{
										err=client.Del(history_json.Device_id[i]).Err()
										if err!=nil{
											txtmessage=err.Error()
											break
										}
										txtmessage="取消訂閱成功!"
										break
									}else{
										var s []string
										s = removeStringInSlice(stringSlice, userID)
										client.Set(history_json.Device_id[i],s,0)
										txtmessage="取消訂閱成功!"
										break
									}
								}else{
									txtmessage="你並沒有訂閱此ID。"
									break
								}
							}
							stringSlice:=strings.Split(val,",")
							if stringInSlice(userID,stringSlice){
								txtmessage="您已訂閱過此ID!"
								break
							} else{
								val=val+","+userID
								client.Set(history_json.Device_id[i],val,0)
								txtmessage="訂閱成功!"
								break
							}
						}
					}
				} else{
					for i:=0; i<len(all_device); i++ {
						if strings.Contains(inText,strings.ToLower(all_device[i].Device_id)) {
							txtmessage="Device_id: "+all_device[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+all_device[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(all_device[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(all_device[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+all_device[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(all_device[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(all_device[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(all_device[i].S_t0),'f',0,64)
							break
						} else if len(all_device[i].SiteName)!=0 && strings.Contains(inText,all_device[i].SiteName){
							txtmessage="Device_id: "+all_device[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+all_device[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(all_device[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(all_device[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+all_device[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(all_device[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(all_device[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(all_device[i].S_t0),'f',0,64)
							break
						}
					}
				}
				if len(txtmessage)==0{
					txtmessage="Sorry! No this device ID, please check again."
				}
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(txtmessage)).Do(); err != nil {
					log.Print(err)
				}
			case *linebot.LocationMessage:
				var txtmessage string
				lat := message.Latitude
				lon := message.Longitude
				minD := math.MaxFloat64
				for i:=0; i<len(all_device); i++ {
					if all_device[i].Gps_lat<=lat+0.05 && all_device[i].Gps_lat>=lat-0.05 && all_device[i].Gps_lon<=lon+0.05 && all_device[i].Gps_lon>=lon-0.05{
						D:=distanceInKmBetweenEarthCoordinates(lat, lon, all_device[i].Gps_lat, all_device[i].Gps_lat)
						if D<minD{
							minD=D
							txtmessage=strconv.FormatFloat(lat)+","+strconv.FormatFloat(lon)
							txtmessage="Device_id: "+all_device[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+all_device[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(all_device[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(all_device[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+all_device[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(all_device[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(all_device[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(all_device[i].S_t0),'f',0,64)
						}
					} else{
						continue
					}
				}
				if len(txtmessage)==0{
					txtmessage="很抱歉這附近1公里內沒有任何上線的AirBox。"
				}
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(txtmessage)).Do(); err != nil {
					log.Print(err)
				}
			}
		}
	}
}

func stringInSlice(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}

func removeStringInSlice(s []string, r string) []string {
    for i, v := range s {
        if v == r {
            return append(s[:i], s[i+1:]...)
        }
    }
    return s
}

func degreesToRadians(degrees float64) float64 {
  return degrees * math.Pi / 180;
}

func distanceInKmBetweenEarthCoordinates(lat1 float64, lon1 float64, lat2 float64, lon2 float64) float64 {
  var earthRadiusKm = 6371.0;

  var dLat = degreesToRadians(lat2-lat1);
  var dLon = degreesToRadians(lon2-lon1);

  lat1 = degreesToRadians(lat1);
  lat2 = degreesToRadians(lat2);

  var a = math.Sin(dLat/2) * math.Sin(dLat/2) +
          math.Sin(dLon/2) * math.Sin(dLon/2) * math.Cos(lat1) * math.Cos(lat2);
  var c = 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a));
  return earthRadiusKm * c;
}

<commit_msg>test gps<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"io/ioutil"
	"encoding/json"
	"strings"
	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/go-redis/redis"
	"strconv"
	"math"
	// "time"
)

type device struct {
	// Gps_num float32 `json:"gps_num"`
	// App string `json:"app"`
	// Gps_alt float32 `json:"gps_alt"`
	// Fmt_opt int `json:"fmt_opt"`
	// Device string `json:"device"`
	// S_d2 float32 `json:"s_d2"`
	S_d0 float32 `json:"s_d0"`
	// S_d1 float32 `json:"s_d1"`
	S_h0 float32 `json:"s_h0"`
	SiteName string `json:"SiteName"`
	// Gps_fix float32 `json:"gps_fix"`
	// Ver_app string `json:"ver_app"`
	Gps_lat float64 `json:"gps_lat"`
	S_t0 float32 `json:"s_t0"`
	Timestamp string `json:"timestamp"`
	Gps_lon float64 `json:"gps_lon"`
	// Date string `json:"date"`
	// Tick float32 `json:"tick"`
	Device_id string `json:"device_id"`
	// S_1 float32 `json:"s_1"`
	// S_0 float32 `json:"s_0"`
	// S_3 float32 `json:"s_3"`
	// S_2 float32 `json:"s_2"`
	// Ver_format string `json:"ver_format"`
	// Time string `json:"time"`
}

type airbox struct {
	Source string `json:"source"`
	Feeds []device `json:"feeds"`
	Version string `json:"version"`
	Num_of_records int `json:"num_of_records"`
}

type subscribeid struct {
	Device_id []string `json:"device_id"`
	Sitename []string `json:"sitename"`
}

var bot *linebot.Client
var airbox_json airbox
var lass_json airbox
var maps_json airbox
var all_device []device
var history_json subscribeid
var	client=redis.NewClient(&redis.Options{
		Addr:"hipposerver.ddns.net:6379",
		Password:"",
		DB:0,
	})

func main() {
	url := "https://data.lass-net.org/data/last-all-airbox.json"
	req, _ := http.NewRequest("GET", url, nil)
	res, _ := http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ := ioutil.ReadAll(res.Body)
	errs := json.Unmarshal(body, &airbox_json)
	if errs != nil {
		fmt.Println(errs)
	}

	url = "https://data.lass-net.org/data/last-all-lass.json"
	req, _ = http.NewRequest("GET", url, nil)
	res, _ = http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	errs = json.Unmarshal(body, &lass_json)
	if errs != nil {
		fmt.Println(errs)
	}

	url = "https://data.lass-net.org/data/last-all-maps.json"
	req, _ = http.NewRequest("GET", url, nil)
	res, _ = http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	errs = json.Unmarshal(body, &maps_json)
	if errs != nil {
		fmt.Println(errs)
	}

	all_device=append(maps_json.Feeds,lass_json.Feeds...)
	all_device=append(all_device,airbox_json.Feeds...)

	url = "https://data.lass-net.org/data/airbox_list.json"
	req, _ = http.NewRequest("GET", url, nil)
	res, _ = http.DefaultClient.Do(req)
	defer res.Body.Close()
	body, _ = ioutil.ReadAll(res.Body)
	errs = json.Unmarshal(body, &history_json)
	if errs != nil {
		fmt.Println(errs)
	}
	// pushmessage()
	// fmt.Println(airbox_json)
	var err error
	bot, err = linebot.New(os.Getenv("ChannelSecret"), os.Getenv("ChannelAccessToken"))
	// _,_=bot.PushMessage("U3617adbdd46283d7e859f36302f4f471", linebot.NewTextMessage("hi!")).Do()
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)

}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	events, err := bot.ParseRequest(r)

	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}

	for _, event := range events {
		if event.Type == linebot.EventTypeMessage {
			switch message := event.Message.(type) {
			case *linebot.TextMessage:
				var txtmessage string
				inText := strings.ToLower(message.Text)
				if strings.Contains(inText,"訂閱"){
					userID:=event.Source.UserID
					// pong, _ := client.Ping().Result()
					// txtmessage=pong
					for i:=0; i<len(history_json.Device_id); i++ {
						if strings.Contains(inText,strings.ToLower(history_json.Device_id[i]))||strings.Contains(inText,history_json.Sitename[i]) {
							val, err:=client.Get(history_json.Device_id[i]).Result()
							if err!=nil{
								if strings.Contains(inText,"取消"){
									txtmessage="你並沒有訂閱此ID。"
									break
								}
								client.Set(history_json.Device_id[i],userID,0)
								txtmessage="訂閱成功!"
								break
							}
							if strings.Contains(inText,"取消"){
								stringSlice:=strings.Split(val,",")
								if stringInSlice(userID,stringSlice){
									if len(stringSlice)==1{
										err=client.Del(history_json.Device_id[i]).Err()
										if err!=nil{
											txtmessage=err.Error()
											break
										}
										txtmessage="取消訂閱成功!"
										break
									}else{
										var s []string
										s = removeStringInSlice(stringSlice, userID)
										client.Set(history_json.Device_id[i],s,0)
										txtmessage="取消訂閱成功!"
										break
									}
								}else{
									txtmessage="你並沒有訂閱此ID。"
									break
								}
							}
							stringSlice:=strings.Split(val,",")
							if stringInSlice(userID,stringSlice){
								txtmessage="您已訂閱過此ID!"
								break
							} else{
								val=val+","+userID
								client.Set(history_json.Device_id[i],val,0)
								txtmessage="訂閱成功!"
								break
							}
						}
					}
				} else{
					for i:=0; i<len(all_device); i++ {
						if strings.Contains(inText,strings.ToLower(all_device[i].Device_id)) {
							txtmessage="Device_id: "+all_device[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+all_device[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(all_device[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(all_device[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+all_device[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(all_device[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(all_device[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(all_device[i].S_t0),'f',0,64)
							break
						} else if len(all_device[i].SiteName)!=0 && strings.Contains(inText,all_device[i].SiteName){
							txtmessage="Device_id: "+all_device[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+all_device[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(all_device[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(all_device[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+all_device[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(all_device[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(all_device[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(all_device[i].S_t0),'f',0,64)
							break
						}
					}
				}
				if len(txtmessage)==0{
					txtmessage="Sorry! No this device ID, please check again."
				}
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(txtmessage)).Do(); err != nil {
					log.Print(err)
				}
			case *linebot.LocationMessage:
				var txtmessage string
				lat := message.Latitude
				lon := message.Longitude
				minD := math.MaxFloat64
				for i:=0; i<len(all_device); i++ {
					if all_device[i].Gps_lat<=lat+0.05 && all_device[i].Gps_lat>=lat-0.05 && all_device[i].Gps_lon<=lon+0.05 && all_device[i].Gps_lon>=lon-0.05{
						D:=distanceInKmBetweenEarthCoordinates(lat, lon, all_device[i].Gps_lat, all_device[i].Gps_lat)
						if D<minD{
							minD=D
							txtmessage=strconv.FormatFloat(lat,'f',3,64)+","+strconv.FormatFloat(lon,'f',3,64)
							txtmessage="Device_id: "+all_device[i].Device_id+"\n"
							txtmessage=txtmessage+"Site Name: "+all_device[i].SiteName+"\n"
							txtmessage=txtmessage+"Location: ("+strconv.FormatFloat(float64(all_device[i].Gps_lon),'f',3,64)+","+strconv.FormatFloat(float64(all_device[i].Gps_lat),'f',3,64)+")"+"\n"
							txtmessage=txtmessage+"Timestamp: "+all_device[i].Timestamp+"\n"
							txtmessage=txtmessage+"PM2.5: "+strconv.FormatFloat(float64(all_device[i].S_d0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Humidity: "+strconv.FormatFloat(float64(all_device[i].S_h0),'f',0,64)+"\n"
							txtmessage=txtmessage+"Temperature: "+strconv.FormatFloat(float64(all_device[i].S_t0),'f',0,64)
						}
					} else{
						continue
					}
				}
				if len(txtmessage)==0{
					txtmessage="很抱歉這附近1公里內沒有任何上線的AirBox。"
				}
				if _, err = bot.ReplyMessage(event.ReplyToken, linebot.NewTextMessage(txtmessage)).Do(); err != nil {
					log.Print(err)
				}
			}
		}
	}
}

func stringInSlice(a string, list []string) bool {
    for _, b := range list {
        if b == a {
            return true
        }
    }
    return false
}

func removeStringInSlice(s []string, r string) []string {
    for i, v := range s {
        if v == r {
            return append(s[:i], s[i+1:]...)
        }
    }
    return s
}

func degreesToRadians(degrees float64) float64 {
  return degrees * math.Pi / 180;
}

func distanceInKmBetweenEarthCoordinates(lat1 float64, lon1 float64, lat2 float64, lon2 float64) float64 {
  var earthRadiusKm = 6371.0;

  var dLat = degreesToRadians(lat2-lat1);
  var dLon = degreesToRadians(lon2-lon1);

  lat1 = degreesToRadians(lat1);
  lat2 = degreesToRadians(lat2);

  var a = math.Sin(dLat/2) * math.Sin(dLat/2) +
          math.Sin(dLon/2) * math.Sin(dLon/2) * math.Cos(lat1) * math.Cos(lat2);
  var c = 2 * math.Atan2(math.Sqrt(a), math.Sqrt(1-a));
  return earthRadiusKm * c;
}

<|endoftext|>


<commit_before>// main.go - onionwrap
//
// To the extent possible under law, Yawning Angel waived all copyright
// and related or neighboring rights to onionwrap, using the creative
// commons "cc0" public domain dedication. See LICENSE or
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.

// onionwrap serves delicious Onion Service Wraps.
package main

import (
	"errors"
	"flag"
	gofmt "fmt"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/yawning/bulb"
	"github.com/yawning/bulb/utils"
)

const (
	controlPortEnv       = "TOR_CONTROL_PORT"
	controlPortPasswdEnv = "TOR_CONTROL_PASSWD"

	localhost          = "127.0.0.1"
	defaultControlPort = "tcp://" + localhost + ":9051"

	sigKillDelay = 5 * time.Second
)

var debugSpew bool
var quietSpew bool
var noRewriteArgs bool

func infof(fmt string, args ...interface{}) {
	if !quietSpew {
		gofmt.Fprintf(os.Stderr, "INFO: "+fmt, args...)
	}
}

func errorf(fmt string, args ...interface{}) {
	gofmt.Fprintf(os.Stderr, "ERROR: "+fmt, args...)
	os.Exit(-1)
}

func debugf(fmt string, args ...interface{}) {
	// This explicitly overrides quietSpew.
	if debugSpew {
		gofmt.Fprintf(os.Stderr, "DEBUG: "+fmt, args...)
	}
}

func parsePort(portStr string) (uint16, error) {
	p, err := strconv.ParseUint(portStr, 10, 16)
	if err != nil {
		return 0, err
	}
	if p == 0 {
		return 0, errors.New("invalid port '0'")
	}
	return uint16(p), nil
}

func parsePortArg(arg string) (virtPort, targetPort, target string, err error) {
	// This is formated as VIRTPORT[,TARGET], which is identical to
	// what the ADD_ONION command expects out of the 'Port' arguments.
	//
	//  * If the 'TARGET' is omitted, 'VIRTPORT' is mirrored.
	//  * If 'TARGET' only a naked port, then '127.0.0.1:TARGET' is used.
	//  * If 'TARGET' has the prefix 'unix:' the rest is treated as a path
	//    for an AF_UNIX socket.
	//  * Otherwise 'TARGET' is parsed as an IP Address/Port.
	//
	// Note: rendservice.c:parse_port_config()'s Doxygen comment lies and
	// specifies 'socket:' as the prefix, but it really is 'unix:'.
	if arg == "" {
		return "", "", "", errors.New("no Onion Service port specified")
	}
	splitArg := strings.SplitN(arg, ",", 2)
	virtPort = splitArg[0]
	if _, err = parsePort(virtPort); err != nil {
		return "", "", "", err
	}
	if len(splitArg) == 1 {
		// Only a 'VIRTPORT' was provided, mirror it onto the target.
		return virtPort, virtPort, localhost + ":" + virtPort, nil
	}

	target = splitArg[1]
	if _, err = parsePort(target); err == nil {
		// The 'TARGET' is a naked port.
		return virtPort, target, localhost + ":" + target, nil
	}
	if unixPath := strings.TrimPrefix(target, "unix:"); unixPath != target {
		return virtPort, "", unixPath, nil
	}
	tcpAddr, err := net.ResolveTCPAddr("tcp", target)
	if err != nil {
		return "", "", "", err
	}
	if tcpAddr.Port == 0 {
		return "", "", "", errors.New("target has invalid port '0'")
	}
	targetPort = strconv.Itoa(tcpAddr.Port)
	return
}

func main() {
	//
	// Parse/validate the command line arguments.
	//

	const controlPortArg = "control-port"
	ctrlPortArg := flag.String(controlPortArg, "", "Tor control port")
	flag.Lookup(controlPortArg).DefValue = defaultControlPort
	hsPortArg := flag.String("port", "", "Onion Service port")
	flag.BoolVar(&debugSpew, "debug", false, "Print debug messages to stderr")
	flag.BoolVar(&quietSpew, "quiet", false, "Suppress non-error messages")
	flag.BoolVar(&noRewriteArgs, "no-rewrite", false, "Disable rewriting subprocess arguments")
	flag.Parse()

	// The control port is taken from the argument, the env var, and then
	// the hardcoded default in that order.
	if *ctrlPortArg == "" {
		*ctrlPortArg = os.Getenv(controlPortEnv)
		if *ctrlPortArg == "" {
			*ctrlPortArg = defaultControlPort
		}
	}
	ctrlNet, ctrlAddr, err := utils.ParseControlPortString(*ctrlPortArg)
	if err != nil {
		errorf("Invalid control port: %v\n", err)
	}

	virtPort, targetPort, target, err := parsePortArg(*hsPortArg)
	if err != nil {
		errorf("Invalid virtual port: %v\n", err)
	}

	// The command that will be fork/execed.
	cmdVec := flag.Args()
	var cmd *exec.Cmd
	switch len(cmdVec) {
	case 0:
		errorf("No command specified to wrap.\n")
	case 1:
		cmd = exec.Command(cmdVec[0])
	default:
		cmd = exec.Command(cmdVec[0], cmdVec[1:]...)
	}
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if !noRewriteArgs {
		// Unless explicitly disabled, replace certain variables in the
		// subprocess command line arguments with values propagated from
		// the onionwrap command line.
		//
		//  * %VPORT - The 'VIRTPORT'.
		//  * %TPORT - The port component of 'TARGET'.
		//  * %TADDR - The entire 'TARGET'.
		for i := 1; i < len(cmd.Args); i++ {
			v := cmd.Args[i]
			v = strings.Replace(v, "%VPORT", virtPort, -1)
			if targetPort != "" {
				// AF_UNIX targets won't have a port.
				v = strings.Replace(v, "%TPORT", targetPort, -1)
			}
			v = strings.Replace(v, "%TADDR", target, -1)
			cmd.Args[i] = v
		}
	}

	debugf("Cmd: %v\n", cmd.Args)
	debugf("CtrlPort: %v, %v\n", ctrlNet, ctrlAddr)
	debugf("VirtPort: %v Target: %v\n", virtPort, target)

	//
	// Do the actual work.
	//

	// Setup the Onion Service, after connecting to the control port.
	ctrlConn, err := bulb.Dial(ctrlNet, ctrlAddr)
	if err != nil {
		errorf("Failed to connect to the control port: %v\n", err)
	}
	defer ctrlConn.Close()
	ctrlConn.Debug(debugSpew)
	if err = ctrlConn.Authenticate(os.Getenv(controlPortPasswdEnv)); err != nil {
		errorf("Failed to authenticate with the control port: %v\n", err)
	}

	// TODO: Support saving the PK/Loading a PK.
	resp, err := ctrlConn.Request("ADD_ONION NEW:BEST Port=%s Flags=DiscardPK", *hsPortArg)
	if err != nil {
		errorf("Failed to create onion service: %v\n", err)
	}
	var serviceID string
	for _, l := range resp.Data {
		serviceID = strings.TrimPrefix(l, "ServiceID=")
		if serviceID != l {
			break
		}
	}
	if serviceID == "" {
		// This should *NEVER* happen since the command succeded, and
		// the spec guarantees that this will be sent.
		errorf("Failed to determine service ID.")
	}
	infof("Created onion: %s.onion:%s -> %s\n", serviceID, virtPort, target)

	// TODO: Wait till the HS descriptor has been published?

	// Initialize the signal handling and launch the process.
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	err = cmd.Start()
	if err != nil {
		os.Exit(-1)
	}
	doneChan := make(chan error)
	go func() {
		doneChan <- cmd.Wait()
	}()

	// Wait for the child to finish, or the wrapper to receive
	// SIGINT/SIGTERM.
	select {
	case <-doneChan:
	case sig := <-sigChan:
		// Propagate the signal to the child, and wait for it to die.
		debugf("received signal: %v\n", sig)
		cmd.Process.Signal(sig)
		select {
		case <-doneChan:
		case <-time.After(sigKillDelay):
			debugf("post signal delay elapsed, killing child\n")
			cmd.Process.Kill()
			os.Exit(-1)
		}
	}

	debugf("child process terminated\n")
	if !cmd.ProcessState.Success() {
		// ProcessState doesn't give the exact return value. :(
		os.Exit(-1)
	}
	os.Exit(0)
}
<commit_msg>Redirect bulb's debug spew to stderr when debug is enabled.<commit_after>// main.go - onionwrap
//
// To the extent possible under law, Yawning Angel waived all copyright
// and related or neighboring rights to onionwrap, using the creative
// commons "cc0" public domain dedication. See LICENSE or
// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.

// onionwrap serves delicious Onion Service Wraps.
package main

import (
	"errors"
	"flag"
	gofmt "fmt"
	"log"
	"net"
	"os"
	"os/exec"
	"os/signal"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/yawning/bulb"
	"github.com/yawning/bulb/utils"
)

const (
	controlPortEnv       = "TOR_CONTROL_PORT"
	controlPortPasswdEnv = "TOR_CONTROL_PASSWD"

	localhost          = "127.0.0.1"
	defaultControlPort = "tcp://" + localhost + ":9051"

	sigKillDelay = 5 * time.Second
)

var debugSpew bool
var quietSpew bool
var noRewriteArgs bool

func infof(fmt string, args ...interface{}) {
	if !quietSpew {
		gofmt.Fprintf(os.Stderr, "INFO: "+fmt, args...)
	}
}

func errorf(fmt string, args ...interface{}) {
	gofmt.Fprintf(os.Stderr, "ERROR: "+fmt, args...)
	os.Exit(-1)
}

func debugf(fmt string, args ...interface{}) {
	// This explicitly overrides quietSpew.
	if debugSpew {
		gofmt.Fprintf(os.Stderr, "DEBUG: "+fmt, args...)
	}
}

func parsePort(portStr string) (uint16, error) {
	p, err := strconv.ParseUint(portStr, 10, 16)
	if err != nil {
		return 0, err
	}
	if p == 0 {
		return 0, errors.New("invalid port '0'")
	}
	return uint16(p), nil
}

func parsePortArg(arg string) (virtPort, targetPort, target string, err error) {
	// This is formated as VIRTPORT[,TARGET], which is identical to
	// what the ADD_ONION command expects out of the 'Port' arguments.
	//
	//  * If the 'TARGET' is omitted, 'VIRTPORT' is mirrored.
	//  * If 'TARGET' only a naked port, then '127.0.0.1:TARGET' is used.
	//  * If 'TARGET' has the prefix 'unix:' the rest is treated as a path
	//    for an AF_UNIX socket.
	//  * Otherwise 'TARGET' is parsed as an IP Address/Port.
	//
	// Note: rendservice.c:parse_port_config()'s Doxygen comment lies and
	// specifies 'socket:' as the prefix, but it really is 'unix:'.
	if arg == "" {
		return "", "", "", errors.New("no Onion Service port specified")
	}
	splitArg := strings.SplitN(arg, ",", 2)
	virtPort = splitArg[0]
	if _, err = parsePort(virtPort); err != nil {
		return "", "", "", err
	}
	if len(splitArg) == 1 {
		// Only a 'VIRTPORT' was provided, mirror it onto the target.
		return virtPort, virtPort, localhost + ":" + virtPort, nil
	}

	target = splitArg[1]
	if _, err = parsePort(target); err == nil {
		// The 'TARGET' is a naked port.
		return virtPort, target, localhost + ":" + target, nil
	}
	if unixPath := strings.TrimPrefix(target, "unix:"); unixPath != target {
		return virtPort, "", unixPath, nil
	}
	tcpAddr, err := net.ResolveTCPAddr("tcp", target)
	if err != nil {
		return "", "", "", err
	}
	if tcpAddr.Port == 0 {
		return "", "", "", errors.New("target has invalid port '0'")
	}
	targetPort = strconv.Itoa(tcpAddr.Port)
	return
}

func main() {
	//
	// Parse/validate the command line arguments.
	//

	const controlPortArg = "control-port"
	ctrlPortArg := flag.String(controlPortArg, "", "Tor control port")
	flag.Lookup(controlPortArg).DefValue = defaultControlPort
	hsPortArg := flag.String("port", "", "Onion Service port")
	flag.BoolVar(&debugSpew, "debug", false, "Print debug messages to stderr")
	flag.BoolVar(&quietSpew, "quiet", false, "Suppress non-error messages")
	flag.BoolVar(&noRewriteArgs, "no-rewrite", false, "Disable rewriting subprocess arguments")
	flag.Parse()

	// The control port is taken from the argument, the env var, and then
	// the hardcoded default in that order.
	if *ctrlPortArg == "" {
		*ctrlPortArg = os.Getenv(controlPortEnv)
		if *ctrlPortArg == "" {
			*ctrlPortArg = defaultControlPort
		}
	}
	ctrlNet, ctrlAddr, err := utils.ParseControlPortString(*ctrlPortArg)
	if err != nil {
		errorf("Invalid control port: %v\n", err)
	}

	virtPort, targetPort, target, err := parsePortArg(*hsPortArg)
	if err != nil {
		errorf("Invalid virtual port: %v\n", err)
	}

	// The command that will be fork/execed.
	cmdVec := flag.Args()
	var cmd *exec.Cmd
	switch len(cmdVec) {
	case 0:
		errorf("No command specified to wrap.\n")
	case 1:
		cmd = exec.Command(cmdVec[0])
	default:
		cmd = exec.Command(cmdVec[0], cmdVec[1:]...)
	}
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if !noRewriteArgs {
		// Unless explicitly disabled, replace certain variables in the
		// subprocess command line arguments with values propagated from
		// the onionwrap command line.
		//
		//  * %VPORT - The 'VIRTPORT'.
		//  * %TPORT - The port component of 'TARGET'.
		//  * %TADDR - The entire 'TARGET'.
		for i := 1; i < len(cmd.Args); i++ {
			v := cmd.Args[i]
			v = strings.Replace(v, "%VPORT", virtPort, -1)
			if targetPort != "" {
				// AF_UNIX targets won't have a port.
				v = strings.Replace(v, "%TPORT", targetPort, -1)
			}
			v = strings.Replace(v, "%TADDR", target, -1)
			cmd.Args[i] = v
		}
	}

	debugf("Cmd: %v\n", cmd.Args)
	debugf("CtrlPort: %v, %v\n", ctrlNet, ctrlAddr)
	debugf("VirtPort: %v Target: %v\n", virtPort, target)

	//
	// Do the actual work.
	//

	// Setup the Onion Service, after connecting to the control port.
	ctrlConn, err := bulb.Dial(ctrlNet, ctrlAddr)
	if err != nil {
		errorf("Failed to connect to the control port: %v\n", err)
	}
	defer ctrlConn.Close()
	if debugSpew {
		log.SetOutput(os.Stderr)
		ctrlConn.Debug(debugSpew)
	}
	if err = ctrlConn.Authenticate(os.Getenv(controlPortPasswdEnv)); err != nil {
		errorf("Failed to authenticate with the control port: %v\n", err)
	}

	// TODO: Support saving the PK/Loading a PK.
	resp, err := ctrlConn.Request("ADD_ONION NEW:BEST Port=%s Flags=DiscardPK", *hsPortArg)
	if err != nil {
		errorf("Failed to create onion service: %v\n", err)
	}
	var serviceID string
	for _, l := range resp.Data {
		serviceID = strings.TrimPrefix(l, "ServiceID=")
		if serviceID != l {
			break
		}
	}
	if serviceID == "" {
		// This should *NEVER* happen since the command succeded, and
		// the spec guarantees that this will be sent.
		errorf("Failed to determine service ID.")
	}
	infof("Created onion: %s.onion:%s -> %s\n", serviceID, virtPort, target)

	// TODO: Wait till the HS descriptor has been published?

	// Initialize the signal handling and launch the process.
	sigChan := make(chan os.Signal)
	signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
	err = cmd.Start()
	if err != nil {
		os.Exit(-1)
	}
	doneChan := make(chan error)
	go func() {
		doneChan <- cmd.Wait()
	}()

	// Wait for the child to finish, or the wrapper to receive
	// SIGINT/SIGTERM.
	select {
	case <-doneChan:
	case sig := <-sigChan:
		// Propagate the signal to the child, and wait for it to die.
		debugf("received signal: %v\n", sig)
		cmd.Process.Signal(sig)
		select {
		case <-doneChan:
		case <-time.After(sigKillDelay):
			debugf("post signal delay elapsed, killing child\n")
			cmd.Process.Kill()
			os.Exit(-1)
		}
	}

	debugf("child process terminated\n")
	if !cmd.ProcessState.Success() {
		// ProcessState doesn't give the exact return value. :(
		os.Exit(-1)
	}
	os.Exit(0)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/chengjf/mymoney/model"
	"time"
)

func main() {
	fmt.Println("Hello, this is my first go application.")
	test()
}

func test() {
	var a = model.Account{
		Name:    "Cash",
		Balance: 100,
	}
	fmt.Println(a)
	var b = model.Account{
		Name: "美丽源",
	}
	fmt.Println(b)

	var c = model.Category{
		Name: "晚饭",
	}
	fmt.Println(c)

	var t = model.Transaction{
		From:       a,
		To:         b,
		Amount:     22,
		CreateDate: time.Now(),
		UpdateDate: time.Now(),
	}

	fmt.Println(t)

}
<commit_msg>Use json string for print info.<commit_after>package main

import (
	"fmt"
	"github.com/chengjf/mymoney/model"
	"time"
	"encoding/json"
)

func main() {
	fmt.Println("Hello, this is my first go application.")
	test()
}

func test() {
	var a = model.Account{
		Name:    "Cash",
		Balance: 100,
	}
	print(a)
	var b = model.Account{
		Name: "美丽源",
	}
	print(b)

	var c = model.Category{
		Name: "晚饭",
	}
	print(c)

	var t = model.Transaction{
		From:       a,
		To:         b,
		Amount:     22,
		CreateDate: time.Now(),
		UpdateDate: time.Now(),
	}

	print(t)

}

func print(v interface{}) {
	if bytes, e := json.MarshalIndent(v,"","\t"); e == nil {
		fmt.Printf("%#s\n", string(bytes))
	}
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"os"
	"os/signal"
	"sync"

	"github.com/Shopify/sarama"
)

func main() {

	consumerBrokers := []string{"kafka-broker-01.ana.kochava.com:9092", "kafka-broker-02.ana.kochava.com:9092", "kafka-broker-03.ana.kochava.com:9092"}
	producerBrokers := []string{"analytics-kafka-v000-9lzw:9092", "analytics-kafka-v000-u40u:9092", "analytics-kafka-v000-wrdq:9092"}

	logFile, err := os.OpenFile("/var/log/firehose/firehose.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer logFile.Close()

	log.SetOutput(logFile)

	metricsFile, err := os.OpenFile("/var/log/firehose/metrics.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer metricsFile.Close()

	log.Println("Getting the Kafka consumer")
	consumer, err := GetKafkaConsumer(consumerBrokers, metricsFile)
	if err != nil {
		log.Println("Unable to create consumer", err)
	}

	log.Println("Getting the Kafka producer")
	producer, err := GetKafkaProducer(producerBrokers, metricsFile)
	if err != nil {
		log.Println("Unable to create producer", err)
	}

	defer CloseConsumer(consumer)
	defer CloseProducer(producer)

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	var wg sync.WaitGroup

	topic := "events"

	transferChan := make(chan sarama.ProducerMessage, 100000)
	for partition := 0; partition < 24; partition++ {

		partitionConsumer, err := consumer.ConsumePartition(topic, int32(partition), sarama.OffsetNewest)
		if err != nil {
			panic(err)
		}

		wg.Add(1)

		go PullFromTopic(partitionConsumer, transferChan, signals, &wg)
		log.Println("Started consumer for partition ", partition)

		wg.Add(2)
		go PushToTopic(producer, transferChan, signals, &wg)
		log.Println("Started producer")
	}

	go MonitorChan(transferChan, signals)

	wg.Wait()
}
<commit_msg>Actually spin up double producer threads<commit_after>package main

import (
	"log"
	"os"
	"os/signal"
	"sync"

	"github.com/Shopify/sarama"
)

func main() {

	consumerBrokers := []string{"kafka-broker-01.ana.kochava.com:9092", "kafka-broker-02.ana.kochava.com:9092", "kafka-broker-03.ana.kochava.com:9092"}
	producerBrokers := []string{"analytics-kafka-v000-9lzw:9092", "analytics-kafka-v000-u40u:9092", "analytics-kafka-v000-wrdq:9092"}

	logFile, err := os.OpenFile("/var/log/firehose/firehose.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer logFile.Close()

	log.SetOutput(logFile)

	metricsFile, err := os.OpenFile("/var/log/firehose/metrics.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		log.Fatalln("Failed to open log file:", err)
	}
	defer metricsFile.Close()

	log.Println("Getting the Kafka consumer")
	consumer, err := GetKafkaConsumer(consumerBrokers, metricsFile)
	if err != nil {
		log.Println("Unable to create consumer", err)
	}

	log.Println("Getting the Kafka producer")
	producer, err := GetKafkaProducer(producerBrokers, metricsFile)
	if err != nil {
		log.Println("Unable to create producer", err)
	}

	defer CloseConsumer(consumer)
	defer CloseProducer(producer)

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	var wg sync.WaitGroup

	topic := "events"

	transferChan := make(chan sarama.ProducerMessage, 100000)
	for partition := 0; partition < 24; partition++ {

		partitionConsumer, err := consumer.ConsumePartition(topic, int32(partition), sarama.OffsetNewest)
		if err != nil {
			panic(err)
		}

		wg.Add(1)

		go PullFromTopic(partitionConsumer, transferChan, signals, &wg)
		log.Println("Started consumer for partition ", partition)

		wg.Add(2)
		go PushToTopic(producer, transferChan, signals, &wg)
		go PushToTopic(producer, transferChan, signals, &wg)
		log.Println("Started producer")
	}

	go MonitorChan(transferChan, signals)

	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"runtime"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()
)

var (
	addr = flag.String("listen-address", ":9101", "The address to listen on for HTTP requests.")

	// seed random number
	r = rand.New(rand.NewSource(time.Now().UnixNano()))

	// define prometheus counter
	httpRequestTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "http_requests_total",
			Help: "Number of handled http requests.",
		},
		[]string{"code", "handler"},
	)
)

func init() {
	// Metrics have to be registered to be exposed:
	prometheus.MustRegister(httpRequestTotal)
}

func main() {

	fmt.Printf("Starting estafette-ci-api (version=%v, branch=%v, revision=%v, buildDate=%v, goVersion=%v)\n", version, branch, revision, buildDate, goVersion)

	// start prometheus
	go func() {
		fmt.Println("Serving Prometheus metrics at :9101/metrics...")
		http.Handle("/metrics", promhttp.Handler())
		log.Fatal(http.ListenAndServe(*addr, nil))
	}()
}
<commit_msg>log github webhook<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"runtime"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	version   string
	branch    string
	revision  string
	buildDate string
	goVersion = runtime.Version()
)

var (
	prometheusAddress     = flag.String("metrics-listen-address", ":9101", "The address to listen on for Prometheus metrics requests.")
	prometheusMetricsPath = flag.String("metrics-path", "/metrics", "The path to listen for Prometheus metrics requests.")
	apiAddress            = flag.String("api-listen-address", ":5000", "The address to listen on for api HTTP requests.")

	// seed random number
	r = rand.New(rand.NewSource(time.Now().UnixNano()))

	// define prometheus counter
	httpRequestTotal = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Name: "http_requests_total",
			Help: "Number of handled http requests.",
		},
		[]string{"code", "handler"},
	)
)

func init() {
	// Metrics have to be registered to be exposed:
	prometheus.MustRegister(httpRequestTotal)
}

func main() {

	fmt.Printf("Starting estafette-ci-api (version=%v, branch=%v, revision=%v, buildDate=%v, goVersion=%v)\n", version, branch, revision, buildDate, goVersion)

	// start prometheus
	go func() {
		fmt.Printf("Serving Prometheus metrics at %v%v...\n", *prometheusAddress, *prometheusMetricsPath)
		http.Handle(*prometheusMetricsPath, promhttp.Handler())
		log.Fatal(http.ListenAndServe(*prometheusAddress, nil))
	}()

	fmt.Printf("Listening at %v for api calls...\n", *apiAddress)
	http.HandleFunc("/webhook/github", githubWebhookHandler)
	log.Fatal(http.ListenAndServe(*apiAddress, nil))
}

func githubWebhookHandler(w http.ResponseWriter, r *http.Request) {

	// https://developer.github.com/webhooks/

	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		log.Println(err)
	}

	fmt.Printf("Received webhook at %v from GitHub: %v\n", r.URL, string(body))
	w.WriteHeader(200)
}
<|endoftext|>


<commit_before>package main

/* TODO:

1. [ ] serve HTTP /v1/instances/:app_id to retrieve ip:port pairs for given app
2. [ ] implement upstream-conf.d file management
3. [ ] implement UDP proxy (one-way/two-way, fanout & roundrobin)
4. [ ] implement TCP proxy (with pluggable impls: haproxy, lvs, ...)

XXX Changes:

* `--marathon-host` is now named `--marathon-ip` and only accepts IP addresses

*/

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"strconv"

	"github.com/christianparpart/serviced/marathon"
	"github.com/gorilla/mux"
	flag "github.com/ogier/pflag"
)

type MmsdService struct {
	MarathonScheme   string
	MarathonIP       net.IP
	MarathonPort     uint
	RunStateDir      string
	GatewayEnabled   bool
	GatewayPortHTTP  uint
	GatewayPortHTTPS uint
	ManagedIP        net.IP
	HaproxyCfg       string
	HaproxyCfgTail   string
	HaproxyPort      uint
	ServiceBind      net.IP
	ServicePort      uint
}

type HealthStatusChangedEvent struct {
	AppId  string
	TaskId string
	Alive  bool
}

func (mmsd *MmsdService) v1_instances(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	name := vars["name"]

	portIndex := 0
	if sval := r.URL.Query().Get("portIndex"); len(sval) != 0 {
		i, err := strconv.Atoi(sval)
		if err == nil {
			portIndex = i
		}
	}

	m, err := marathon.NewService(mmsd.MarathonIP, mmsd.MarathonPort)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	app, err := m.GetApp(name)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Printf("GetApp error. %v\n", err)
		return
	}

	if app == nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	// appJson, err := json.MarshalIndent(app, "", " ")
	// w.Write(appJson)
	// fmt.Fprintf(w, "\n")
	// return

	for _, task := range app.Tasks {
		fmt.Fprintf(w, "%v:%v\n", task.Host, task.Ports[portIndex])
	}
}

func (mmsd *MmsdService) SetupSSE() {
	var baseUrl string = "http://rack5-gateway:8080"
	var sse *EventSource = NewEventSource(fmt.Sprintf("%s/v2/events", baseUrl))

	sse.OnOpen = func(event, data string) {
		log.Printf("OnOpen event '%v': %+v\n", event, data)
	}
	sse.OnMessage = func(event, data string) {
		//log.Printf("OnMessage event '%v': %+v\n", event, data)
		log.Printf("OnMessage event '%v'\n", event)
	}
	sse.OnError = func(event, data string) {
		log.Printf("OnError: '%v': %+v\n", event, data)
	}

	sse.AddEventListener("event_stream_attached", func(data string) {
		log.Printf("sse client attached: %+v\n", data)
	})

	sse.AddEventListener("event_stream_detached", func(data string) {
		log.Printf("sse client detached: %+v\n", data)
	})

	sse.AddEventListener("health_status_changed_event", func(data string) {
		var event HealthStatusChangedEvent
		json.Unmarshal([]byte(data), &event)
		log.Printf("Health Status Changed: %+v\n", event)
	})

	go sse.RunForever()
}

func (mmsd *MmsdService) Run() {
	flag.IPVar(&mmsd.MarathonIP, "marathon-ip", mmsd.MarathonIP, "Marathon endpoint TCP IP address")
	flag.UintVar(&mmsd.MarathonPort, "marathon-port", mmsd.MarathonPort, "Marathon endpoint TCP port number")
	flag.StringVar(&mmsd.RunStateDir, "run-state-dir", mmsd.RunStateDir, "Path to directory to keep run-state")
	flag.IPVar(&mmsd.ManagedIP, "managed-ip", mmsd.ManagedIP, "IP-address to manage for mmsd")
	flag.BoolVar(&mmsd.GatewayEnabled, "gateway", mmsd.GatewayEnabled, "Enables gateway support")
	flag.UintVar(&mmsd.GatewayPortHTTP, "gateway-http-port", mmsd.GatewayPortHTTP, "gateway HTTP port")
	flag.UintVar(&mmsd.GatewayPortHTTPS, "gateway-https-port", mmsd.GatewayPortHTTPS, "gateway HTTP port")

	flag.StringVar(&mmsd.HaproxyCfg, "haproxy-cfg", mmsd.HaproxyCfg, "path to haproxy config file")
	flag.StringVar(&mmsd.HaproxyCfgTail, "haproxy-cfgtail", mmsd.HaproxyCfgTail, "path to haproxy tail config file")
	flag.IPVar(&mmsd.ServiceBind, "haproxy-bind", mmsd.ServiceBind, "haproxy management port")
	flag.UintVar(&mmsd.HaproxyPort, "haproxy-port", mmsd.HaproxyPort, "haproxy management port")

	flag.Parse()

	mmsd.SetupSSE()

	// HTTP service
	router := mux.NewRouter()
	v1 := router.PathPrefix("/v1").Subrouter()

	v1.HandleFunc("/instances{name:/.*}", mmsd.v1_instances).Methods("GET")

	serviceAddr := fmt.Sprintf("%v:%v", mmsd.ServiceBind, mmsd.ServicePort)
	log.Printf("Service listening on http://%v\n", serviceAddr)

	http.ListenAndServe(serviceAddr, router)
}

func main() {
	var mmsd MmsdService = MmsdService{
		MarathonScheme:   "http",
		MarathonIP:       net.ParseIP("127.0.0.1"),
		MarathonPort:     8080,
		RunStateDir:      "/var/run/mmsd",
		GatewayEnabled:   false,
		GatewayPortHTTP:  80,
		GatewayPortHTTPS: 443,
		HaproxyCfg:       "/var/run/mmsd/haproxy.cfg",
		HaproxyCfgTail:   "/etc/mmsd/haproxy-tail.cfg",
		HaproxyPort:      8081,
		ServiceBind:      net.ParseIP("0.0.0.0"),
		ServicePort:      8082,
	}

	mmsd.Run()
}
<commit_msg>configuration<commit_after>package main

/* TODO:

1. [ ] serve HTTP /v1/instances/:app_id to retrieve ip:port pairs for given app
2. [ ] implement upstream-conf.d file management
3. [ ] implement UDP proxy (one-way/two-way, fanout & roundrobin)
4. [ ] implement TCP proxy (with pluggable impls: haproxy, lvs, ...)

XXX Changes:

* `--marathon-host` is now named `--marathon-ip` and only accepts IP addresses

*/

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"net/http"
	"strconv"

	"github.com/christianparpart/serviced/marathon"
	"github.com/gorilla/mux"
	flag "github.com/ogier/pflag"
)

type MmsdService struct {
	MarathonScheme   string
	MarathonIP       net.IP
	MarathonPort     uint
	RunStateDir      string
	GatewayEnabled   bool
	GatewayPortHTTP  uint
	GatewayPortHTTPS uint
	ManagedIP        net.IP
	HaproxyCfg       string
	HaproxyCfgTail   string
	HaproxyPort      uint
	ServiceBind      net.IP
	ServicePort      uint
}

type HealthStatusChangedEvent struct {
	AppId  string
	TaskId string
	Alive  bool
}

func (mmsd *MmsdService) v1_instances(w http.ResponseWriter, r *http.Request) {
	vars := mux.Vars(r)
	name := vars["name"]

	portIndex := 0
	if sval := r.URL.Query().Get("portIndex"); len(sval) != 0 {
		i, err := strconv.Atoi(sval)
		if err == nil {
			portIndex = i
		}
	}

	m, err := marathon.NewService(mmsd.MarathonIP, mmsd.MarathonPort)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	app, err := m.GetApp(name)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		log.Printf("GetApp error. %v\n", err)
		return
	}

	if app == nil {
		w.WriteHeader(http.StatusNotFound)
		return
	}

	// appJson, err := json.MarshalIndent(app, "", " ")
	// w.Write(appJson)
	// fmt.Fprintf(w, "\n")
	// return

	for _, task := range app.Tasks {
		fmt.Fprintf(w, "%v:%v\n", task.Host, task.Ports[portIndex])
	}
}

func (mmsd *MmsdService) SetupSSE() {
	var url string = fmt.Sprintf("http://%v:%v/v2/events",
		mmsd.MarathonIP, mmsd.MarathonPort)

	var sse *EventSource = NewEventSource(url)

	sse.OnOpen = func(event, data string) {
		log.Printf("OnOpen event '%v': %+v\n", event, data)
	}
	sse.OnMessage = func(event, data string) {
		//log.Printf("OnMessage event '%v': %+v\n", event, data)
		log.Printf("OnMessage event '%v'\n", event)
	}
	sse.OnError = func(event, data string) {
		log.Printf("OnError: '%v': %+v\n", event, data)
	}

	sse.AddEventListener("event_stream_attached", func(data string) {
		log.Printf("sse client attached: %+v\n", data)
	})

	sse.AddEventListener("event_stream_detached", func(data string) {
		log.Printf("sse client detached: %+v\n", data)
	})

	sse.AddEventListener("health_status_changed_event", func(data string) {
		var event HealthStatusChangedEvent
		json.Unmarshal([]byte(data), &event)
		log.Printf("Health Status Changed: %+v\n", event)
	})

	go sse.RunForever()
}

func (mmsd *MmsdService) Run() {
	flag.IPVar(&mmsd.MarathonIP, "marathon-ip", mmsd.MarathonIP, "Marathon endpoint TCP IP address")
	flag.UintVar(&mmsd.MarathonPort, "marathon-port", mmsd.MarathonPort, "Marathon endpoint TCP port number")
	flag.StringVar(&mmsd.RunStateDir, "run-state-dir", mmsd.RunStateDir, "Path to directory to keep run-state")
	flag.IPVar(&mmsd.ManagedIP, "managed-ip", mmsd.ManagedIP, "IP-address to manage for mmsd")
	flag.BoolVar(&mmsd.GatewayEnabled, "gateway", mmsd.GatewayEnabled, "Enables gateway support")
	flag.UintVar(&mmsd.GatewayPortHTTP, "gateway-http-port", mmsd.GatewayPortHTTP, "gateway HTTP port")
	flag.UintVar(&mmsd.GatewayPortHTTPS, "gateway-https-port", mmsd.GatewayPortHTTPS, "gateway HTTP port")

	flag.StringVar(&mmsd.HaproxyCfg, "haproxy-cfg", mmsd.HaproxyCfg, "path to haproxy config file")
	flag.StringVar(&mmsd.HaproxyCfgTail, "haproxy-cfgtail", mmsd.HaproxyCfgTail, "path to haproxy tail config file")
	flag.IPVar(&mmsd.ServiceBind, "haproxy-bind", mmsd.ServiceBind, "haproxy management port")
	flag.UintVar(&mmsd.HaproxyPort, "haproxy-port", mmsd.HaproxyPort, "haproxy management port")

	flag.Parse()

	mmsd.SetupSSE()

	// HTTP service
	router := mux.NewRouter()
	v1 := router.PathPrefix("/v1").Subrouter()

	v1.HandleFunc("/instances{name:/.*}", mmsd.v1_instances).Methods("GET")

	serviceAddr := fmt.Sprintf("%v:%v", mmsd.ServiceBind, mmsd.ServicePort)
	log.Printf("Service listening on http://%v\n", serviceAddr)

	http.ListenAndServe(serviceAddr, router)
}

func main() {
	var mmsd MmsdService = MmsdService{
		MarathonScheme:   "http",
		MarathonIP:       net.ParseIP("127.0.0.1"),
		MarathonPort:     8080,
		RunStateDir:      "/var/run/mmsd",
		GatewayEnabled:   false,
		GatewayPortHTTP:  80,
		GatewayPortHTTPS: 443,
		HaproxyCfg:       "/var/run/mmsd/haproxy.cfg",
		HaproxyCfgTail:   "/etc/mmsd/haproxy-tail.cfg",
		HaproxyPort:      8081,
		ServiceBind:      net.ParseIP("0.0.0.0"),
		ServicePort:      8082,
	}

	mmsd.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/thoj/go-ircevent"
	"math/rand"
	"os"
	"strings"
	"time"
)

type Config struct {
	Server   string
	Channels []string
	Realname string
	Nick     string
	Prefix   string
}

func main() {

	file, ferr := os.Open("config.json")
	if ferr != nil {
		fmt.Println(ferr)
	}
	decoder := json.NewDecoder(file)
	config := Config{}
	err := decoder.Decode(&config)
	if err != nil {
		fmt.Println(err)
	}

	ircobj := irc.IRC(config.Nick, config.Realname)

	error := ircobj.Connect(config.Server)
	if error != nil {
		fmt.Println(error)
	}
	ircobj.AddCallback("001", func(e *irc.Event) {
		for _, channel := range config.Channels {
			fmt.Println("Joining ", channel)
			ircobj.Join(channel)
		}
	})

	go ircobj.AddCallback("PRIVMSG", func(e *irc.Event) {
		message := e.Message()
		if strings.HasPrefix(message, config.Prefix) {
			message = strings.TrimPrefix(message, config.Prefix)

			if message == "inflik" {
				ircobj.Privmsg(e.Arguments[0], "Jag skulle bara vilja inflika för ett ögonblick.")
				ircobj.Privmsg(e.Arguments[0], "Vad du kallar Linux är faktiskt GNU/Linux, eller som jag själv kallar det, GNU+Linux. Linux är inte ett operativsystem i sig själv, utan snarare ännu en del utav ett funktionellt GNU-system, som görs användbart av GNU corelibs, shell-utils, och andra nödvändiga delar, som tillsammans definerar ett OS enligt POSIX.")
				ircobj.Privmsg(e.Arguments[0], "Många datorer kör ett modifierat GNU-system varje dag, utan att inse det. Genom en lustig härva av händelser kallas det GNU som används ofta Linux, och många av dess användare inser inte att de använder GNU-systemet, som utvecklats utav GNU-projektet.")
				ircobj.Privmsg(e.Arguments[0], "Det finns ett Linux, och dessa människor använder det, men det är bara en del av systemet de använder. Linux är kerneln, programmet i systemet som allokerar maskinens resurser till de andra programmen du kör. Kerneln är en viktig del utav ett operativsystem, men helt oanvändbart i sig själv; den kan bara fungera i samband med ett helt operativsystem.")
				ircobj.Privmsg(e.Arguments[0], "Linux används oftast i samband med GNU-operativsystemet: hela systemet är bara GNU med Linux tillagt, eller GNU/Linux. Alla så kallade Linux-distrubitioner är egentligen distrubutioner utav GNU/Linux!")
			}
			if message == "blasera" {
				ircobj.Action(e.Arguments[0], "420blaserar det gröna")
			}
			if message == "kommandon" || message == "hjälp" {
				ircobj.Privmsg(e.Arguments[0], "Nuvarande kommandon: blasera, kattljud, pälsknulla <offer>, älska <offer>, fluffa <offer>")
			}
			if strings.Replace(message, " ", "", -1) == "pälsknulla" {
				ircobj.Privmsg(e.Arguments[0], "pälsknulla tar ett argument - offret")
			} else if strings.Fields(message)[0] == "pälsknulla" {
				target := strings.Fields(message)[1]
				var pälsknullastring = pälsknulla(target)
				ircobj.Action(e.Arguments[0], pälsknullastring)
			}

			if strings.Replace(message, " ", "", -1) == "fluffa" {
				ircobj.Privmsg(e.Arguments[0], "fluffa tar ett argument - offret")
			} else if strings.Fields(message)[0] == "fluffa" {
				target := strings.Fields(message)[1]
				var fluffastring = "fluffar " + target
				ircobj.Action(e.Arguments[0], fluffastring)
			}

			if strings.Replace(message, " ", "", -1) == "älska" {
				ircobj.Privmsg(e.Arguments[0], "älska tar ett argument - offret")
			} else if strings.Fields(message)[0] == "älska" {
				target := strings.Fields(message)[1]
				var älskastring = älska(target)
				ircobj.Action(e.Arguments[0], älskastring)
			}
			if message == "kattljud" {
				ircobj.Privmsg(e.Arguments[0], kattljud())
			}
		}
	})
	ircobj.Loop()
}

func pälsknulla(person string) string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	var action = []string{"biter", "slickar", "penetrerar", "leker med", "spräcker", "misshandlar", "kyssar", "våldtar", "glider in i", "drar ut sigsjälv från", "tänder eld på", "gnuggar sig själv mot"}
	var description = []string{"skrumpna", "illadoftande", "unga", "slitna", "stinkande", "enorma", "minimala", "söta", "lösa", "mjuka", "droppande", "brännheta", "iskalla", "regnbågsfärgade"}
	var object = []string{"murrhål", "armhåla", "ansikte", "navel", "kloak", "gentooinstallation", "tunga", "livmoder", "strumpa", "avgasrör", "plåttermos", ".... Skit samma, jag vill inte detta egentligen."}

	if !strings.HasSuffix(person, "s") {
		person = person + "s"
	}
	hack := []string{action[r.Intn(len(action))], person, description[r.Intn(len(description))], object[r.Intn(len(object))]}
	return strings.Join(hack, " ")

}

func älska(person string) string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	var action = []string{"pussar", "slickar", "gosar", "smeker", "lindar armarna om", "kramar", "klappar", "myser", "sniffar", "nafsar", "gnuggar", "eskimåkysser"}
	var description = []string{"försiktigt", "mjukt", "hårt", "lugnt", "hjärtfyllt", "älskande", "varmt", "gulligt", "lyckligt"}

	hack := []string{action[r.Intn(len(action))], person, description[r.Intn(len(description))], "♥"}
	return strings.Join(hack, " ")

}

func kattljud() string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	var noises = []string{"mew", "meow", "mrawr", "rawr", "rar", "mrowr", "rwr", "mrw", "mrwrw"}
	return noises[r.Intn(len(noises))]
}
<commit_msg>bot är mord<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/thoj/go-ircevent"
	"math/rand"
	"os"
	"strings"
	"time"
)

type Config struct {
	Server   string
	Channels []string
	Realname string
	Nick     string
	Prefix   string
}

func main() {

	file, ferr := os.Open("config.json")
	if ferr != nil {
		fmt.Println(ferr)
	}
	decoder := json.NewDecoder(file)
	config := Config{}
	err := decoder.Decode(&config)
	if err != nil {
		fmt.Println(err)
	}

	ircobj := irc.IRC(config.Nick, config.Realname)

	error := ircobj.Connect(config.Server)
	if error != nil {
		fmt.Println(error)
	}
	ircobj.AddCallback("001", func(e *irc.Event) {
		for _, channel := range config.Channels {
			fmt.Println("Joining ", channel)
			ircobj.Join(channel)
		}
	})

	go ircobj.AddCallback("PRIVMSG", func(e *irc.Event) {
		message := e.Message()
		if strings.HasPrefix(message, config.Prefix) {
			message = strings.TrimPrefix(message, config.Prefix)

			if message == "inflik" {
				ircobj.Privmsg(e.Arguments[0], "Jag skulle bara vilja inflika för ett ögonblick.")
				ircobj.Privmsg(e.Arguments[0], "Vad du kallar Linux är faktiskt GNU/Linux, eller som jag själv kallar det, GNU+Linux. Linux är inte ett operativsystem i sig själv, utan snarare ännu en del utav ett funktionellt GNU-system, som görs användbart av GNU corelibs, shell-utils, och andra nödvändiga delar, som tillsammans definerar ett OS enligt POSIX.")
				ircobj.Privmsg(e.Arguments[0], "Många datorer kör ett modifierat GNU-system varje dag, utan att inse det. Genom en lustig härva av händelser kallas det GNU som används ofta Linux, och många av dess användare inser inte att de använder GNU-systemet, som utvecklats utav GNU-projektet.")
				ircobj.Privmsg(e.Arguments[0], "Det finns ett Linux, och dessa människor använder det, men det är bara en del av systemet de använder. Linux är kerneln, programmet i systemet som allokerar maskinens resurser till de andra programmen du kör. Kerneln är en viktig del utav ett operativsystem, men helt oanvändbart i sig själv; den kan bara fungera i samband med ett helt operativsystem.")
				ircobj.Privmsg(e.Arguments[0], "Linux används oftast i samband med GNU-operativsystemet: hela systemet är bara GNU med Linux tillagt, eller GNU/Linux. Alla så kallade Linux-distrubitioner är egentligen distrubutioner utav GNU/Linux!")
			}
			if message == "blasera" {
				ircobj.Action(e.Arguments[0], "420blaserar det gröna")
			}
			if message == "kommandon" || message == "hjälp" {
				ircobj.Privmsg(e.Arguments[0], "Nuvarande kommandon: blasera, kattljud, pälsknulla <offer>, älska <offer>, fluffa <offer>")
			}
			if strings.Replace(message, " ", "", -1) == "pälsknulla" {
				ircobj.Privmsg(e.Arguments[0], "pälsknulla tar ett argument - offret")
			} else if strings.Fields(message)[0] == "pälsknulla" {
				target := strings.Fields(message)[1]
				var pälsknullastring = pälsknulla(target)
				ircobj.Action(e.Arguments[0], pälsknullastring)
			}

			if strings.Replace(message, " ", "", -1) == "fluffa" {
				ircobj.Privmsg(e.Arguments[0], "fluffa tar ett argument - offret")
			} else if strings.Fields(message)[0] == "fluffa" {
				target := strings.Fields(message)[1]
				var fluffastring = "fluffar " + target
				ircobj.Action(e.Arguments[0], fluffastring)
			}

			if strings.Replace(message, " ", "", -1) == "älska" {
				ircobj.Privmsg(e.Arguments[0], "älska tar ett argument - offret")
			} else if strings.Fields(message)[0] == "älska" {
				target := strings.Fields(message)[1]
				var älskastring = älska(target)
				ircobj.Action(e.Arguments[0], älskastring)
			}
			if message == "kattljud" {
				ircobj.Privmsg(e.Arguments[0], kattljud())
			}
			if strings.Replace(message, " ", "", -1) == "mörda" {
				ircobj.Privmsg(e.Arguments[0], "mörda tar ett argument - offret")
			} else if strings.Fields(message)[0] == "mörda" {
				target := strings.Fields(message)[1]
				var mördastring = mörda(target)
				ircobj.Action(e.Arguments[0], mördastring)

			}
		}
	})
	ircobj.Loop()
}

func pälsknulla(person string) string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	var action = []string{"biter", "slickar", "penetrerar", "leker med", "spräcker", "misshandlar", "kyssar", "våldtar", "glider in i", "drar ut sigsjälv från", "tänder eld på", "gnuggar sig själv mot"}
	var description = []string{"skrumpna", "illadoftande", "unga", "slitna", "stinkande", "enorma", "minimala", "söta", "lösa", "mjuka", "droppande", "brännheta", "iskalla", "regnbågsfärgade"}
	var object = []string{"murrhål", "armhåla", "ansikte", "navel", "kloak", "gentooinstallation", "tunga", "livmoder", "strumpa", "avgasrör", "plåttermos", ".... Skit samma, jag vill inte detta egentligen."}

	if !strings.HasSuffix(person, "s") {
		person = person + "s"
	}
	hack := []string{action[r.Intn(len(action))], person, description[r.Intn(len(description))], object[r.Intn(len(object))]}
	return strings.Join(hack, " ")

}

func älska(person string) string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))

	var action = []string{"pussar", "slickar", "gosar", "smeker", "lindar armarna om", "kramar", "klappar", "myser", "sniffar", "nafsar", "gnuggar", "eskimåkysser"}
	var description = []string{"försiktigt", "mjukt", "hårt", "lugnt", "hjärtfyllt", "älskande", "varmt", "gulligt", "lyckligt"}

	hack := []string{action[r.Intn(len(action))], person, description[r.Intn(len(description))], "♥"}
	return strings.Join(hack, " ")

}

func kattljud() string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	var noises = []string{"mew", "meow", "mrawr", "rawr", "rar", "mrowr", "rwr", "mrw", "mrwrw"}
	return noises[r.Intn(len(noises))]
}
func mörda(person string) string {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	var action = []string{"mördar", "slaktar", "lemlestar", "krossar", "mosar", "hugger", "cuttar", "tystar ner", "fimpar", "utplånar", "eliminerar", "dräper"}
	var description = []string{"försiktigt", "mjukt", "hårt", "lugnt", "hjärtfyllt", "älskande", "varmt", "gulligt", "lyckligt"}
	hack := []string{action[r.Intn(len(action))], person, description[r.Intn(len(description))], "♥"}
	return strings.Join(hack, " ")
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"html"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	strID := os.Getenv("ChannelID")
	numID, err := strconv.ParseInt(strID, 10, 64)
	if err != nil {
		log.Fatal("Wrong environment setting about ChannelID")
	}

	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)

}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()

		//Add with new friend.
		if content != nil && content.IsOperation && content.OpType == linebot.OpTypeAddedAsFriend {
			out := fmt.Sprintf("Thanks for add StackOverflow BOT. Please type technical questions. The BOT will response a similar issue in StackOverflow website. The link can be clicked to see detail resolutions.")
			_, err = bot.SendText([]string{result.RawContent.Params[0]}, out)
			if err != nil {
				log.Println(err)
			}
			log.Println("New friend add, send cue to new friend.")
		}

		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {
			text, err := content.TextContent()

			log.Println("INPUT = " + text.Text)

			var outputString = stackoverflow(text.Text)

			log.Println("OUTPUT = " + outputString)

			_, err = bot.SendText([]string{content.From}, outputString)
			if err != nil {
				log.Println(err)
			}
		}
	}
}

//Items:
type jsonobject struct {
	Items []Item
}

//Item
type Item struct {
	Link  string `json:"link"`
	Title string `json:"title"`
}

func stackoverflow(input string) string {

	root := "http://api.stackexchange.com/2.2/similar"
	para := "?page=1&pagesize=1&order=desc&sort=relevance&site=stackoverflow&title=" + url.QueryEscape(input)

	stackoverflowEndPoint := root + para

	resp, err := http.Get(stackoverflowEndPoint)
	if err != nil {
		log.Println(err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var i jsonobject
	err = json.Unmarshal(body, &i)
	if err != nil {
		log.Println(err)
	}

	var ret string

	if len(i.Items) == 0 {
		ret = "Sorry, I can't find relevant solutions, please specify your question."
	} else {
		ret = html.UnescapeString(i.Items[0].Title) + " " + i.Items[0].Link
	}

	if len(ret) == 0 {
		ret = "Sorry, I can't find relevant solutions, please specify your question."
	}

	if strings.ToLower(input) == "hello" {
		ret = input + " +1"
	}

	return ret
}
<commit_msg>no message<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"html"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strings"

	"github.com/line/line-bot-sdk-go/linebot"
)

var bot *linebot.Client

func main() {
	bot, err = linebot.NewClient(numID, os.Getenv("ChannelSecret"), os.Getenv("MID"))
	log.Println("Bot:", bot, " err:", err)
	http.HandleFunc("/callback", callbackHandler)
	port := os.Getenv("PORT")
	addr := fmt.Sprintf(":%s", port)
	http.ListenAndServe(addr, nil)

}

func callbackHandler(w http.ResponseWriter, r *http.Request) {
	received, err := bot.ParseRequest(r)
	if err != nil {
		if err == linebot.ErrInvalidSignature {
			w.WriteHeader(400)
		} else {
			w.WriteHeader(500)
		}
		return
	}
	for _, result := range received.Results {
		content := result.Content()

		//Add with new friend.
		if content != nil && content.IsOperation && content.OpType == linebot.OpTypeAddedAsFriend {
			out := fmt.Sprintf("Thanks for add StackOverflow BOT. Please type technical questions. The BOT will response a similar issue in StackOverflow website. The link can be clicked to see detail resolutions.")
			_, err = bot.SendText([]string{result.RawContent.Params[0]}, out)
			if err != nil {
				log.Println(err)
			}
			log.Println("New friend add, send cue to new friend.")
		}

		if content != nil && content.IsMessage && content.ContentType == linebot.ContentTypeText {
			text, err := content.TextContent()

			log.Println("INPUT = " + text.Text)

			var outputString = stackoverflow(text.Text)

			log.Println("OUTPUT = " + outputString)

			_, err = bot.SendText([]string{content.From}, outputString)
			if err != nil {
				log.Println(err)
			}
		}
	}
}

//Items:
type jsonobject struct {
	Items []Item
}

//Item
type Item struct {
	Link  string `json:"link"`
	Title string `json:"title"`
}

func stackoverflow(input string) string {

	root := "http://api.stackexchange.com/2.2/similar"
	para := "?page=1&pagesize=1&order=desc&sort=relevance&site=stackoverflow&title=" + url.QueryEscape(input)

	stackoverflowEndPoint := root + para

	resp, err := http.Get(stackoverflowEndPoint)
	if err != nil {
		log.Println(err)
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Println(err)
	}

	var i jsonobject
	err = json.Unmarshal(body, &i)
	if err != nil {
		log.Println(err)
	}

	var ret string

	if len(i.Items) == 0 {
		ret = "Sorry, I can't find relevant solutions, please specify your question."
	} else {
		ret = html.UnescapeString(i.Items[0].Title) + " " + i.Items[0].Link
	}

	if len(ret) == 0 {
		ret = "Sorry, I can't find relevant solutions, please specify your question."
	}

	if strings.ToLower(input) == "hello" {
		ret = input + " +1"
	}

	return ret
}
<|endoftext|>