Updated the way to parse doors and windows
All checks were successful
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Successful in 22s
All checks were successful
Golan Testing / testing (1.24.x, ubuntu-latest) (push) Successful in 22s
This commit is contained in:
@ -404,6 +404,10 @@ func (c *Client) GetVehicleByVIN(vin string) *Vehicle {
|
|||||||
SubscriptionFeatures: vData.SubscriptionFeatures,
|
SubscriptionFeatures: vData.SubscriptionFeatures,
|
||||||
client: c,
|
client: c,
|
||||||
}
|
}
|
||||||
|
vehicle.Doors = make(map[string]Door)
|
||||||
|
vehicle.Windows = make(map[string]Window)
|
||||||
|
vehicle.Tires = make(map[string]Tire)
|
||||||
|
vehicle.ClimateProfiles = make(map[string]ClimateProfile)
|
||||||
vehicle.GetVehicleStatus()
|
vehicle.GetVehicleStatus()
|
||||||
vehicle.GetVehicleCondition()
|
vehicle.GetVehicleCondition()
|
||||||
// TODO: Temporary disabled for getting successful testing results
|
// TODO: Temporary disabled for getting successful testing results
|
||||||
|
284
vehicle.go
284
vehicle.go
@ -71,10 +71,10 @@ type Vehicle struct {
|
|||||||
MPG float64 // STATUS REQUEST > "avgFuelConsumptionMpg": 18.5
|
MPG float64 // STATUS REQUEST > "avgFuelConsumptionMpg": 18.5
|
||||||
LP100Km float64 // STATUS REQUEST > "avgFuelConsumptionLitersPer100Kilometers": 12.7
|
LP100Km float64 // STATUS REQUEST > "avgFuelConsumptionLitersPer100Kilometers": 12.7
|
||||||
}
|
}
|
||||||
ClimateProfiles []*ClimateProfile
|
ClimateProfiles map[string]ClimateProfile
|
||||||
Doors []*Door // CONDITION REQUEST >
|
Doors map[string]Door // CONDITION REQUEST >
|
||||||
Windows []*Window // CONDITION REQUEST >
|
Windows map[string]Window // CONDITION REQUEST >
|
||||||
Tires []*Tire // CONDITION AND STATUS REQUEST >
|
Tires map[string]Tire // CONDITION AND STATUS REQUEST >
|
||||||
GeoLocation GeoLocation
|
GeoLocation GeoLocation
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
client *Client
|
client *Client
|
||||||
@ -120,7 +120,8 @@ type GeoLocation struct {
|
|||||||
type Door struct {
|
type Door struct {
|
||||||
Position string // front | rear | boot | enginehood
|
Position string // front | rear | boot | enginehood
|
||||||
SubPosition string // right | left
|
SubPosition string // right | left
|
||||||
Status string
|
Status string // CLOSED |
|
||||||
|
Lock string // LOCKED |
|
||||||
Updated time.Time
|
Updated time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,14 +164,14 @@ func (v *Vehicle) String() string {
|
|||||||
vString += "Litres per 100 km: " + fmt.Sprintf("%v", v.FuelConsumptionAvg.LP100Km) + "\n"
|
vString += "Litres per 100 km: " + fmt.Sprintf("%v", v.FuelConsumptionAvg.LP100Km) + "\n"
|
||||||
|
|
||||||
vString += "=== WINDOWS =====================\n"
|
vString += "=== WINDOWS =====================\n"
|
||||||
for i, w := range v.Windows {
|
for k, v := range v.Windows {
|
||||||
vString += fmt.Sprintf("%d >> %+v\n", i+1, w)
|
vString += fmt.Sprintf("%s >> %+v\n", k, v)
|
||||||
// fmt.Printf("%d >> %+v\n", i+1, w)
|
// fmt.Printf("%d >> %+v\n", i+1, w)
|
||||||
}
|
}
|
||||||
|
|
||||||
vString += "=== DOORS =====================\n"
|
vString += "=== DOORS =====================\n"
|
||||||
for i, d := range v.Doors {
|
for k, v := range v.Doors {
|
||||||
vString += fmt.Sprintf("%d >> %+v\n", i+1, d)
|
vString += fmt.Sprintf("%s >> %+v\n", k, v)
|
||||||
// fmt.Printf("%d >> %+v\n", i+1, d)
|
// fmt.Printf("%d >> %+v\n", i+1, d)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -466,12 +467,12 @@ func (v *Vehicle) GetClimatePresets() {
|
|||||||
var climateProfile ClimateProfile
|
var climateProfile ClimateProfile
|
||||||
json.Unmarshal([]byte(child.Data().(string)), &climateProfile)
|
json.Unmarshal([]byte(child.Data().(string)), &climateProfile)
|
||||||
|
|
||||||
if v.isEV() && climateProfile.VehicleType == "phev" {
|
// if v.isEV() && climateProfile.VehicleType == "phev" {
|
||||||
v.ClimateProfiles = append(v.ClimateProfiles, &climateProfile)
|
// v.ClimateProfiles = append(v.ClimateProfiles, climateProfile)
|
||||||
}
|
// }
|
||||||
if !v.isEV() && climateProfile.VehicleType == "gas" {
|
// if !v.isEV() && climateProfile.VehicleType == "gas" {
|
||||||
v.ClimateProfiles = append(v.ClimateProfiles, &climateProfile)
|
// v.ClimateProfiles = append(v.ClimateProfiles, climateProfile)
|
||||||
}
|
// }
|
||||||
v.Updated = time.Now()
|
v.Updated = time.Now()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -498,12 +499,12 @@ func (v *Vehicle) GetClimateUserPresets() {
|
|||||||
var climateProfile ClimateProfile
|
var climateProfile ClimateProfile
|
||||||
json.Unmarshal([]byte(child.Data().(string)), &climateProfile)
|
json.Unmarshal([]byte(child.Data().(string)), &climateProfile)
|
||||||
|
|
||||||
if v.isEV() && climateProfile.VehicleType == "phev" {
|
// if v.isEV() && climateProfile.VehicleType == "phev" {
|
||||||
v.ClimateProfiles = append(v.ClimateProfiles, &climateProfile)
|
// v.ClimateProfiles = append(v.ClimateProfiles, climateProfile)
|
||||||
}
|
// }
|
||||||
if !v.isEV() && climateProfile.VehicleType == "gas" {
|
// if !v.isEV() && climateProfile.VehicleType == "gas" {
|
||||||
v.ClimateProfiles = append(v.ClimateProfiles, &climateProfile)
|
// v.ClimateProfiles = append(v.ClimateProfiles, climateProfile)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
v.Updated = time.Now()
|
v.Updated = time.Now()
|
||||||
// // ONLY FOR THAT REQUEST BECAUSE OF API SENDS BACK ESCAPING DATA IN DATA FIELD
|
// // ONLY FOR THAT REQUEST BECAUSE OF API SENDS BACK ESCAPING DATA IN DATA FIELD
|
||||||
@ -563,87 +564,83 @@ func (v *Vehicle) GetVehicleStatus() {
|
|||||||
for key, child := range respParsed.S("data").ChildrenMap() {
|
for key, child := range respParsed.S("data").ChildrenMap() {
|
||||||
fmt.Printf("key: %v, value: %v\n", key, child.Data())
|
fmt.Printf("key: %v, value: %v\n", key, child.Data())
|
||||||
if child.Data() == "NOT_EQUIPPED" || child.Data() == "UNKNOWN" || child.Data() == "16383" || child.Data() == "65535" || child.Data() == "None" || child.Data() == "-64.0" || child.Data() == nil {
|
if child.Data() == "NOT_EQUIPPED" || child.Data() == "UNKNOWN" || child.Data() == "16383" || child.Data() == "65535" || child.Data() == "None" || child.Data() == "-64.0" || child.Data() == nil {
|
||||||
fmt.Println("Skipping")
|
// fmt.Println("Skipping")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, "door") && strings.HasSuffix(key, "Position") {
|
if strings.HasPrefix(key, "door") && strings.HasSuffix(key, "Position") {
|
||||||
submatchall := re.FindAllString(key, -1)
|
pos := strings.TrimPrefix(key, "door")
|
||||||
|
pos = strings.TrimSuffix(pos, "Position")
|
||||||
|
submatchall := re.FindAllString(pos, -1)
|
||||||
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
||||||
for _, element := range submatchall {
|
|
||||||
fmt.Println(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
newdoor := Door{}
|
if door, ok := v.Doors[pos]; ok {
|
||||||
newdoor.Position = submatchall[0]
|
door.Status = child.Data().(string)
|
||||||
if len(submatchall) >= 3 {
|
door.Updated = time.Now()
|
||||||
newdoor.SubPosition = submatchall[1]
|
} else {
|
||||||
}
|
door.Status = child.Data().(string)
|
||||||
|
door.Updated = time.Now()
|
||||||
for _, door := range v.Doors {
|
v.Doors[pos] = Door{
|
||||||
if door.Position == newdoor.Position && door.SubPosition == newdoor.SubPosition {
|
Position: submatchall[0],
|
||||||
door.Status = child.Data().(string)
|
Status: child.Data().(string),
|
||||||
door.Updated = time.Now()
|
Updated: time.Now(),
|
||||||
} else {
|
}
|
||||||
newdoor.Status = child.Data().(string)
|
if len(submatchall) >= 2 {
|
||||||
newdoor.Updated = time.Now()
|
if d, ok := v.Doors[pos]; ok {
|
||||||
v.Doors = append(v.Doors, &newdoor)
|
d.SubPosition = submatchall[1]
|
||||||
|
v.Doors[pos] = d
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, "door") && strings.HasSuffix(key, "Status") {
|
if strings.HasPrefix(key, "door") && strings.HasSuffix(key, "LockStatus") {
|
||||||
submatchall := re.FindAllString(key, -1)
|
pos := strings.TrimPrefix(key, "door")
|
||||||
|
pos = strings.TrimSuffix(pos, "LockStatus")
|
||||||
|
submatchall := re.FindAllString(pos, -1)
|
||||||
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
||||||
for _, element := range submatchall {
|
|
||||||
fmt.Println(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
newdoor := Door{}
|
if door, ok := v.Doors[pos]; ok {
|
||||||
newdoor.Position = submatchall[0]
|
door.Lock = child.Data().(string)
|
||||||
if len(submatchall) >= 3 {
|
door.Updated = time.Now()
|
||||||
newdoor.SubPosition = submatchall[1]
|
} else {
|
||||||
}
|
door.Lock = child.Data().(string)
|
||||||
|
door.Updated = time.Now()
|
||||||
for _, door := range v.Doors {
|
v.Doors[pos] = Door{
|
||||||
if door.Position == newdoor.Position && door.SubPosition == newdoor.SubPosition {
|
Position: submatchall[0],
|
||||||
door.Status = child.Data().(string)
|
Lock: child.Data().(string),
|
||||||
door.Updated = time.Now()
|
Updated: time.Now(),
|
||||||
} else {
|
}
|
||||||
newdoor.Status = child.Data().(string)
|
if len(submatchall) >= 2 {
|
||||||
newdoor.Updated = time.Now()
|
if d, ok := v.Doors[pos]; ok {
|
||||||
v.Doors = append(v.Doors, &newdoor)
|
d.SubPosition = submatchall[1]
|
||||||
|
v.Doors[pos] = d
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, "window") && strings.HasSuffix(key, "Status") {
|
if strings.HasPrefix(key, "window") && strings.HasSuffix(key, "Status") {
|
||||||
submatchall := re.FindAllString(key, -1)
|
submatchall := re.FindAllString(key, -1)
|
||||||
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
||||||
for _, element := range submatchall {
|
|
||||||
fmt.Println(element)
|
|
||||||
}
|
|
||||||
|
|
||||||
newwindow := Window{}
|
if window, ok := v.Windows[submatchall[0]+submatchall[1]]; ok {
|
||||||
newwindow.Position = submatchall[0]
|
window.Status = child.Data().(string)
|
||||||
if len(submatchall) >= 3 {
|
window.Updated = time.Now()
|
||||||
newwindow.SubPosition = submatchall[1]
|
} else {
|
||||||
}
|
window.Status = child.Data().(string)
|
||||||
|
window.Updated = time.Now()
|
||||||
for _, window := range v.Windows {
|
v.Windows[submatchall[0]+submatchall[1]] = Window{
|
||||||
if window.Position == newwindow.Position && window.SubPosition == newwindow.SubPosition {
|
Position: submatchall[0],
|
||||||
window.Status = child.Data().(string)
|
SubPosition: submatchall[1],
|
||||||
window.Updated = time.Now()
|
Status: child.Data().(string),
|
||||||
} else {
|
Updated: time.Now(),
|
||||||
newwindow.Status = child.Data().(string)
|
|
||||||
newwindow.Updated = time.Now()
|
|
||||||
v.Windows = append(v.Windows, &newwindow)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, "tire") && !strings.HasSuffix(key, "Psi") {
|
if strings.HasPrefix(key, "tire") && !strings.HasSuffix(key, "Psi") {
|
||||||
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data())
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data())
|
||||||
submatchall := re.FindAllString(key, -1)
|
// submatchall := re.FindAllString(key, -1)
|
||||||
for _, element := range submatchall {
|
// for _, element := range submatchall {
|
||||||
fmt.Println(element)
|
// fmt.Println(element)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -715,94 +712,85 @@ func (v *Vehicle) GetVehicleCondition() {
|
|||||||
for key, child := range respParsed.S("data").S("result").ChildrenMap() {
|
for key, child := range respParsed.S("data").S("result").ChildrenMap() {
|
||||||
fmt.Printf("key: %v, value: %v\n", key, child.Data())
|
fmt.Printf("key: %v, value: %v\n", key, child.Data())
|
||||||
if child.Data() == "NOT_EQUIPPED" || child.Data() == "UNKNOWN" || child.Data() == "16383" || child.Data() == "65535" || child.Data() == "None" || child.Data() == "-64.0" || child.Data() == nil {
|
if child.Data() == "NOT_EQUIPPED" || child.Data() == "UNKNOWN" || child.Data() == "16383" || child.Data() == "65535" || child.Data() == "None" || child.Data() == "-64.0" || child.Data() == nil {
|
||||||
fmt.Println("Skipping")
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, "door") && strings.HasSuffix(key, "Position") {
|
if strings.HasPrefix(key, "door") && strings.HasSuffix(key, "Position") {
|
||||||
submatchall := re.FindAllString(key, -1)
|
pos := strings.TrimPrefix(key, "door")
|
||||||
|
pos = strings.TrimSuffix(pos, "Position")
|
||||||
|
submatchall := re.FindAllString(pos, -1)
|
||||||
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
||||||
for _, element := range submatchall {
|
|
||||||
fmt.Println(element)
|
|
||||||
}
|
|
||||||
newdoor := Door{}
|
|
||||||
newdoor.Position = submatchall[0]
|
|
||||||
if len(submatchall) >= 3 {
|
|
||||||
newdoor.SubPosition = submatchall[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, door := range v.Doors {
|
if door, ok := v.Doors[pos]; ok {
|
||||||
if door.Position == newdoor.Position && door.SubPosition == newdoor.SubPosition {
|
door.Status = child.Data().(string)
|
||||||
door.Status = child.Data().(string)
|
door.Updated = time.Now()
|
||||||
door.Updated = time.Now()
|
} else {
|
||||||
} else {
|
door.Status = child.Data().(string)
|
||||||
newdoor.Status = child.Data().(string)
|
door.Updated = time.Now()
|
||||||
newdoor.Updated = time.Now()
|
v.Doors[pos] = Door{
|
||||||
v.Doors = append(v.Doors, &newdoor)
|
Position: submatchall[0],
|
||||||
|
Status: child.Data().(string),
|
||||||
|
Updated: time.Now(),
|
||||||
|
}
|
||||||
|
if len(submatchall) >= 2 {
|
||||||
|
if d, ok := v.Doors[pos]; ok {
|
||||||
|
d.SubPosition = submatchall[1]
|
||||||
|
v.Doors[pos] = d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(key, "door") && strings.HasSuffix(key, "LockStatus") {
|
||||||
|
pos := strings.TrimPrefix(key, "door")
|
||||||
|
pos = strings.TrimSuffix(pos, "LockStatus")
|
||||||
|
submatchall := re.FindAllString(pos, -1)
|
||||||
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
||||||
|
|
||||||
|
if door, ok := v.Doors[pos]; ok {
|
||||||
|
door.Lock = child.Data().(string)
|
||||||
|
door.Updated = time.Now()
|
||||||
|
} else {
|
||||||
|
door.Lock = child.Data().(string)
|
||||||
|
door.Updated = time.Now()
|
||||||
|
v.Doors[pos] = Door{
|
||||||
|
Position: submatchall[0],
|
||||||
|
Lock: child.Data().(string),
|
||||||
|
Updated: time.Now(),
|
||||||
|
}
|
||||||
|
if len(submatchall) >= 2 {
|
||||||
|
if d, ok := v.Doors[pos]; ok {
|
||||||
|
d.SubPosition = submatchall[1]
|
||||||
|
v.Doors[pos] = d
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, "window") && strings.HasSuffix(key, "Status") {
|
if strings.HasPrefix(key, "window") && strings.HasSuffix(key, "Status") {
|
||||||
submatchall := re.FindAllString(key, -1)
|
submatchall := re.FindAllString(key, -1)
|
||||||
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data(), "number", len(submatchall))
|
||||||
for _, element := range submatchall {
|
|
||||||
fmt.Println(element)
|
|
||||||
}
|
|
||||||
newwindow := Window{}
|
|
||||||
newwindow.Position = submatchall[0]
|
|
||||||
if len(submatchall) >= 3 {
|
|
||||||
newwindow.SubPosition = submatchall[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, window := range v.Windows {
|
if window, ok := v.Windows[submatchall[0]+submatchall[1]]; ok {
|
||||||
if window.Position == newwindow.Position && window.SubPosition == newwindow.SubPosition {
|
window.Status = child.Data().(string)
|
||||||
window.Status = child.Data().(string)
|
window.Updated = time.Now()
|
||||||
window.Updated = time.Now()
|
} else {
|
||||||
} else {
|
window.Status = child.Data().(string)
|
||||||
newwindow.Status = child.Data().(string)
|
window.Updated = time.Now()
|
||||||
newwindow.Updated = time.Now()
|
v.Windows[submatchall[0]+submatchall[1]] = Window{
|
||||||
v.Windows = append(v.Windows, &newwindow)
|
Position: submatchall[0],
|
||||||
|
SubPosition: submatchall[1],
|
||||||
|
Status: child.Data().(string),
|
||||||
|
Updated: time.Now(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if strings.HasPrefix(key, "tire") && !strings.HasSuffix(key, "Unit") {
|
if strings.HasPrefix(key, "tire") && !strings.HasSuffix(key, "Unit") {
|
||||||
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data())
|
v.client.logger.Debug("VEHICLE COND", "key", key, "data", child.Data())
|
||||||
submatchall := re.FindAllString(key, -1)
|
// submatchall := re.FindAllString(key, -1)
|
||||||
for _, element := range submatchall {
|
// for _, element := range submatchall {
|
||||||
fmt.Println(element)
|
// fmt.Println(element)
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
v.Updated = time.Now()
|
v.Updated = time.Now()
|
||||||
}
|
}
|
||||||
|
|
||||||
// vCon := VehicleCondition{}
|
|
||||||
// vcString := respParsed.Path("data.result").String()
|
|
||||||
// json.Unmarshal([]byte(vcString), &vCon)
|
|
||||||
|
|
||||||
// for _, elem := range vCon.VehicleStatus {
|
|
||||||
// if elem.Value == "NOT_EQUIPPED" || elem.Value == "UNKNOWN" || elem.Value == "16383" || elem.Value == "65535" || elem.Value == "None" || elem.Value == "-64.0" {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// if strings.HasPrefix(elem.Key, "door") {
|
|
||||||
// logger.Debugf("VEHICLE COND: %v > %v\n", elem.Key, elem.Value)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for _, elem := range vCon.VehicleStatus {
|
|
||||||
// if elem.Value == "NOT_EQUIPPED" || elem.Value == "UNKNOWN" || elem.Value == "16383" || elem.Value == "65535" || elem.Value == "None" || elem.Value == "-64.0" {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// if strings.HasPrefix(elem.Key, "window") {
|
|
||||||
// logger.Debugf("VEHICLE COND: %v > %v\n", elem.Key, elem.Value)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// for _, elem := range vCon.VehicleStatus {
|
|
||||||
// if elem.Value == "NOT_EQUIPPED" || elem.Value == "UNKNOWN" || elem.Value == "16383" || elem.Value == "65535" || elem.Value == "None" || elem.Value == "-64.0" {
|
|
||||||
// continue
|
|
||||||
// }
|
|
||||||
// if strings.HasPrefix(elem.Key, "tire") {
|
|
||||||
// logger.Debugf("VEHICLE COND: %v > %v\n", elem.Key, elem.Value)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// logger.Debugf("VEHICLE COND REQUEST: %+v", vCon)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// VEHICLE_STATE_TYPE >> IGNITION_OFF
|
// VEHICLE_STATE_TYPE >> IGNITION_OFF
|
||||||
|
Reference in New Issue
Block a user