You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
399 lines
12 KiB
399 lines
12 KiB
package main |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"strconv" |
|
"strings" |
|
"time" |
|
|
|
"github.com/google/uuid" |
|
"samhofi.us/x/keybase/v2/types/chat1" |
|
) |
|
|
|
func reset(m chat1.MsgSummary) { |
|
_, err := k.KVDelete(&m.Channel.Name, "teslabot", "authtok") |
|
if err != nil { |
|
handleError(err, m, "There was an error resetting your authentication. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
_, err = k.KVDelete(&m.Channel.Name, "teslabot", "startPass") |
|
if err != nil { |
|
handleError(err, m, "There was an error resetting your authentication. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
k.SendMessageByConvID(m.ConvID, "Your credentials have been reset successfully.") |
|
} |
|
|
|
func authenticate(m chat1.MsgSummary) { |
|
defer log.PanicSafe() |
|
if isAuthenticated(m) { |
|
k.SendMessageByConvID(m.ConvID, "You have already authenticated (please use !reset to reset.") |
|
return |
|
} |
|
if !m.IsEphemeral { |
|
k.SendMessageByConvID(m.ConvID, "Please remember to delete your message after we have authenticated!") |
|
} |
|
parts := strings.Split(m.Content.Text.Body, " ") |
|
if len(parts) != 3 { |
|
k.SendMessageByConvID(m.ConvID, "Invalid input for command authenticate. Requires username and password. This information is not stored in keybase, or logged.") |
|
return |
|
} |
|
username := parts[1] |
|
password := parts[2] |
|
t, err := login(context.Background(), username, password) |
|
if err != nil { |
|
handleError(err, m, "There was an error logging in. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
log.LogDebug("Token created for %+v", m.Sender.Username) |
|
_, err = k.KVPut(&m.Channel.Name, "teslabot", "authtok", t) |
|
if err != nil { |
|
handleError(err, m, "There was an error storing your auth token. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
k.ReactByConvID(m.ConvID, m.Id, ":car:") |
|
k.DeleteByConvID(m.ConvID, m.Id) |
|
k.SendMessageByConvID(m.ConvID, "You're all set!") |
|
} |
|
|
|
func listVehicles(m chat1.MsgSummary) { |
|
c := getTeslaClient(m) |
|
if c == nil { |
|
return |
|
} |
|
v, err := c.Vehicles() |
|
if err != nil { |
|
handleError(err, m, "There was an error listing vehicles. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
ret := "Detected vehicles for account: ```" |
|
for _, v := range v { |
|
ret += fmt.Sprintf("VIN: %s\n", v.Vin) |
|
ret += fmt.Sprintf("Name: %s\n\n", v.DisplayName) |
|
} |
|
ret += "```" |
|
k.SendMessageByConvID(m.ConvID, ret) |
|
} |
|
|
|
func deferTime(m chat1.MsgSummary) { |
|
parts := strings.Split(m.Content.Text.Body, " ") |
|
t := parts[1] |
|
start := time.Now() |
|
command := strings.Join(parts[2:], " ") |
|
m.Content.Text.Body = fmt.Sprintf("!%+v", command) |
|
timer, err := time.ParseDuration(t) |
|
if err != nil { |
|
handleError(err, m, "There was an error parsing your time input. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
k.SendMessageByConvID(m.ConvID, fmt.Sprintf("I will run `%+v` at %+v", command, time.Now().Add(timer).Format("Jan _2 15:04:05"))) |
|
time.Sleep(timer) |
|
k.SendMessageByConvID(m.ConvID, fmt.Sprintf("Running `%+v` from %+v", command, start.Format("Jan _2 15:04:05"))) |
|
handleChat(m) |
|
} |
|
|
|
func honk(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
err := v.HonkHorn() |
|
if err != nil { |
|
handleError(err, m, "There was an error honking your horn. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
k.SendMessageByConvID(m.ConvID, "I've honked your horn!") |
|
} |
|
|
|
func chargeStatus(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
state, err := v.ChargeState() |
|
if err != nil { |
|
handleError(err, m, "There was an error getting charge state. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
ret := fmt.Sprintf("Status for %+v: ```", v.DisplayName) |
|
ret += fmt.Sprintf("\nCurrent Charge: %+v (%+vmi)", state.BatteryLevel, state.BatteryRange) |
|
if state.ChargingState != "Disconnected" { |
|
ret += fmt.Sprintf("\nConnected Cable: %+v", state.ConnChargeCable) |
|
ret += fmt.Sprintf("\nCharging State: %+v", state.ChargingState) |
|
if state.ChargingState != "Stopped" { |
|
ret += fmt.Sprintf("\nTime to full: %+vmin", state.MinutesToFullCharge) |
|
if state.FastChargerPresent { |
|
ret += fmt.Sprintf("\nFast Charger: %+v %+v", state.FastChargerBrand, state.FastChargerType) |
|
} |
|
} |
|
} |
|
ret += "```\n" |
|
if state.BatteryHeaterOn { |
|
ret += "The battery heater is on. " |
|
} |
|
if state.ChargePortDoorOpen && state.ChargingState == "Disconnected" { |
|
ret += "The charge port is open. " |
|
} |
|
k.SendMessageByConvID(m.ConvID, ret) |
|
} |
|
|
|
func flashLights(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
err := v.FlashLights() |
|
if err != nil { |
|
handleError(err, m, "There was an error flashing your lights. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
|
|
k.SendMessageByConvID(m.ConvID, "I've flashed your lights!") |
|
} |
|
|
|
func currentTemp(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
guiSettings, err := v.GuiSettings() |
|
if err != nil { |
|
handleError(err, m, "There was an error getting your preferences. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
climateState, err := v.ClimateState() |
|
if err != nil { |
|
handleError(err, m, "There was an error getting your Climate State. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
tempSetting := climateState.DriverTempSetting |
|
insideTemp := climateState.InsideTemp |
|
if guiSettings.GuiTemperatureUnits == "F" { |
|
tempSetting = (climateState.DriverTempSetting * 1.8) + 32 |
|
insideTemp = (climateState.InsideTemp * 1.8) + 32 |
|
} |
|
if climateState.IsClimateOn { |
|
k.SendMessageByConvID(m.ConvID, "Your climate on and set to %.0f, current temp is: %.0f inside %+v", |
|
tempSetting, insideTemp, v.DisplayName) |
|
} else { |
|
k.SendMessageByConvID(m.ConvID, "Your climate off but set to %.0f, current temp is %.0f inside %+v", |
|
tempSetting, insideTemp, v.DisplayName) |
|
} |
|
} |
|
|
|
func setClimate(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
guiSettings, err := v.GuiSettings() |
|
if err != nil { |
|
handleError(err, m, "There was an error getting your preferences. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
parts := strings.Split(m.Content.Text.Body, " ") |
|
for _, val := range parts { |
|
if val == "on" { |
|
err := v.StartAirConditioning() |
|
if err != nil { |
|
handleError(err, m, "There was an error starting your Climate. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} |
|
if val == "off" { |
|
err := v.StopAirConditioning() |
|
if err != nil { |
|
handleError(err, m, "There was an error turning off your Climate. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} |
|
if temp, err := strconv.Atoi(val); err == nil { |
|
if guiSettings.GuiTemperatureUnits == "F" { |
|
temp = (temp - 32) * 5 / 9 |
|
} |
|
err = v.SetTemperature(float64(temp), float64(temp)) |
|
if err != nil { |
|
handleError(err, m, "There was an error setting your Climate. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
err = v.StartAirConditioning() |
|
if err != nil { |
|
handleError(err, m, "There was an error starting your Climate. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} |
|
} |
|
climateState, err := v.ClimateState() |
|
if err != nil { |
|
handleError(err, m, "There was an error getting your Climate State. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
tempSetting := climateState.DriverTempSetting |
|
insideTemp := climateState.InsideTemp |
|
if guiSettings.GuiTemperatureUnits == "F" { |
|
tempSetting = (climateState.DriverTempSetting * 1.8) + 32 |
|
insideTemp = (climateState.InsideTemp * 1.8) + 32 |
|
} |
|
if climateState.IsClimateOn { |
|
k.SendMessageByConvID(m.ConvID, "Your climate on and set to %.0f, current temp is: %.0f", tempSetting, insideTemp) |
|
} else { |
|
k.SendMessageByConvID(m.ConvID, "Your climate off but set to %.0f", tempSetting) |
|
} |
|
|
|
} |
|
|
|
func lockVehicle(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
err := v.LockDoors() |
|
if err != nil { |
|
handleError(err, m, "There was an error locking your doors. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} |
|
|
|
func unlockVehicle(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
err := v.UnlockDoors() |
|
if err != nil { |
|
handleError(err, m, "There was an error unlocking your doors. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} |
|
|
|
func startCharge(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
state, err := v.ChargeState() |
|
if err != nil { |
|
handleError(err, m, "There was an error getting charge state. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
if state.ChargingState != "Disconnected" && state.FastChargerBrand != "Tesla" { |
|
err := v.StartCharging() |
|
if err != nil { |
|
handleError(err, m, "There was an error starting your charge. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} else { |
|
k.SendMessageByConvID(m.ConvID, "You must plug in to an L1/L2 charger to use this command.") |
|
} |
|
|
|
} |
|
|
|
func stopCharge(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
state, err := v.ChargeState() |
|
if err != nil { |
|
handleError(err, m, "There was an error getting charge state. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
if state.ChargingState != "Disconnected" && state.FastChargerBrand != "Tesla" { |
|
err := v.StopCharging() |
|
if err != nil { |
|
handleError(err, m, "There was an error stopping your charge. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} else { |
|
k.SendMessageByConvID(m.ConvID, "You must plug in to an L1/L2 charger to use this command.") |
|
} |
|
} |
|
|
|
func enableStart(m chat1.MsgSummary) { |
|
parts := strings.Split(m.Content.Text.Body, " ") |
|
if len(parts) != 3 { |
|
k.SendMessageByConvID(m.ConvID, "You must 'accept' this command and supply your password. This command is not as safe, as it stores your password in KVStore.") |
|
return |
|
} |
|
if !strings.Contains(parts[1], "accept") { |
|
k.SendMessageByConvID(m.ConvID, "You must 'accept' this command and supply your password. This command is not as safe, as it stores your password in KVStore.") |
|
return |
|
} |
|
_, err := k.KVPut(&m.Channel.Name, "teslabot", "startPass", parts[2]) |
|
if err != nil { |
|
handleError(err, m, "There was an error storing your password. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
|
|
} |
|
|
|
func disableStart(m chat1.MsgSummary) { |
|
_, err := k.KVDelete(&m.Channel.Name, "teslabot", "startPass") |
|
if err != nil { |
|
handleError(err, m, "There was an error deleting your password. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
} |
|
|
|
func startVehicle(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
if v == nil { |
|
return |
|
} |
|
test, _ := k.KVGet(&m.Channel.Name, "teslabot", "startPass") |
|
if test.EntryValue == "" { |
|
k.SendMessageByConvID(m.ConvID, "You must first !enablestart to use this command.") |
|
return |
|
} |
|
err := v.Start(test.EntryValue) |
|
if err != nil { |
|
handleError(err, m, "There was an error starting your vehicle. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
k.SendMessageByConvID(m.ConvID, "Your vehicle has been started!") |
|
|
|
} |
|
|
|
func openTrunk(m chat1.MsgSummary) { |
|
v := getVehicle(m) |
|
|
|
parts := strings.Split(m.Content.Text.Body, " ") |
|
|
|
if len(parts) != 2 { |
|
k.SendMessageByConvID(m.ConvID, "You must supply front or rear.") |
|
return |
|
} |
|
|
|
trunk := parts[1] |
|
|
|
switch trunk { |
|
case "rear": |
|
case "front": |
|
err := v.OpenTrunk(strings.ToLower(trunk)) |
|
if err != nil { |
|
handleError(err, m, "There was an error opening your trunk. Contact @rudi9719 for more information with code %+v") |
|
return |
|
} |
|
default: |
|
k.SendMessageByConvID(m.ConvID, "You must supply front or rear.") |
|
return |
|
} |
|
|
|
} |
|
|
|
func handleError(err error, m chat1.MsgSummary, msg string) { |
|
tracker := uuid.NewString() |
|
log.LogError("%+v: %+v", tracker, err) |
|
if strings.HasPrefix(err.Error(), "405") { |
|
k.SendMessageByConvID(m.ConvID, "Tesla returned an error. Please ensure your vehicle isn't currently being serviced.") |
|
} else if strings.HasPrefix(err.Error(), "400") { |
|
k.SendMessageByConvID(m.ConvID, "Tesla returned an error. The command you tried to use may need an update. Please contact @rudi9719 with tracking ID %+v and the bad command.", tracker) |
|
} else if !m.Content.Attachment.Uploaded && strings.HasPrefix(err.Error(), "408") { |
|
k.SendMessageByConvID(m.ConvID, "Unable to wake vehicle within the timeframe. Trying to run the command again.") |
|
m.Content.Attachment.Uploaded = true |
|
handleChat(m) |
|
} else { |
|
k.SendMessageByConvID(m.ConvID, msg, tracker) |
|
} |
|
}
|
|
|