Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6807cd35ad | ||
|
|
877bce4d1a | ||
|
|
55f7b609b1 |
@@ -6,7 +6,7 @@ using BlubbFish.Utils.IoT.Bots;
|
|||||||
using BlubbFish.Utils.IoT.Bots.Events;
|
using BlubbFish.Utils.IoT.Bots.Events;
|
||||||
using BlubbFish.Utils.IoT.Bots.Moduls;
|
using BlubbFish.Utils.IoT.Bots.Moduls;
|
||||||
|
|
||||||
namespace ZwayBot.Moduls {
|
namespace BlubbFish.IoT.Bots.ZwayBot.Moduls {
|
||||||
internal class CronJob : CronJob<ZwayController> {
|
internal class CronJob : CronJob<ZwayController> {
|
||||||
public override event ModulEvent Update;
|
public override event ModulEvent Update;
|
||||||
|
|
||||||
@@ -27,5 +27,9 @@ namespace ZwayBot.Moduls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void EventLibSetter() { }
|
||||||
|
|
||||||
|
protected override void LibUpadteThread(Object state) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+21
-7
@@ -12,8 +12,9 @@ using BlubbFish.Utils.IoT.Events;
|
|||||||
using BlubbFish.Utils.IoT.Interfaces;
|
using BlubbFish.Utils.IoT.Interfaces;
|
||||||
using LitJson;
|
using LitJson;
|
||||||
|
|
||||||
namespace ZwayBot.Moduls {
|
namespace BlubbFish.IoT.Bots.ZwayBot.Moduls {
|
||||||
class Mqtt : Mqtt<ZwayController> {
|
class Mqtt : Mqtt<ZwayController> {
|
||||||
|
protected Boolean mqttConnect = false;
|
||||||
public override event ModulEvent Update;
|
public override event ModulEvent Update;
|
||||||
|
|
||||||
public Mqtt(ZwayController zwave, InIReader settings) : base(zwave, settings) { }
|
public Mqtt(ZwayController zwave, InIReader settings) : base(zwave, settings) { }
|
||||||
@@ -21,15 +22,15 @@ namespace ZwayBot.Moduls {
|
|||||||
#region Mqtt
|
#region Mqtt
|
||||||
protected override void Connect() {
|
protected override void Connect() {
|
||||||
this.mqtt = ABackend.GetInstance(this.config["settings"], ABackend.BackendType.Data);
|
this.mqtt = ABackend.GetInstance(this.config["settings"], ABackend.BackendType.Data);
|
||||||
this.mqtt.MessageIncomming += this.EventInput;
|
this.mqtt.MessageIncomming += this.MqttUpdate;
|
||||||
this.library.Update += this.EventOutput;
|
this.mqttConnect = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Disconnect() {
|
protected override void Disconnect() {
|
||||||
|
this.mqttConnect = false;
|
||||||
if (this.mqtt != null) {
|
if (this.mqtt != null) {
|
||||||
this.mqtt.MessageIncomming -= this.EventInput;
|
this.mqtt.MessageIncomming -= this.MqttUpdate;
|
||||||
}
|
}
|
||||||
this.library.Update -= this.EventOutput;
|
|
||||||
if (this.mqtt != null) {
|
if (this.mqtt != null) {
|
||||||
this.mqtt.Dispose();
|
this.mqtt.Dispose();
|
||||||
}
|
}
|
||||||
@@ -38,7 +39,14 @@ namespace ZwayBot.Moduls {
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
protected virtual void EventOutput(Object sender, DeviceUpdateEvent e) {
|
public override void EventLibSetter() {
|
||||||
|
this.library.Update += this.HandleLibUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LibUpadteThread(Object state) {
|
||||||
|
try {
|
||||||
|
if(this.mqttConnect) {
|
||||||
|
DeviceUpdateEvent e = state as DeviceUpdateEvent;
|
||||||
String topic = "";
|
String topic = "";
|
||||||
String data = "";
|
String data = "";
|
||||||
if (e.Parent.GetType().HasInterface(typeof(IMqtt))) {
|
if (e.Parent.GetType().HasInterface(typeof(IMqtt))) {
|
||||||
@@ -51,8 +59,12 @@ namespace ZwayBot.Moduls {
|
|||||||
this.Update?.Invoke(this, new MqttEvent(topic, data));
|
this.Update?.Invoke(this, new MqttEvent(topic, data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
protected virtual void EventInput(Object sender, BackendEvent e) {
|
protected virtual void MqttUpdate(Object sender, BackendEvent e) {
|
||||||
|
try {
|
||||||
|
if (this.mqttConnect) {
|
||||||
if (e.From.ToString().StartsWith("zwavebot/devices/") && (e.From.ToString().EndsWith("/set") || e.From.ToString().EndsWith("/get"))) {
|
if (e.From.ToString().StartsWith("zwavebot/devices/") && (e.From.ToString().EndsWith("/set") || e.From.ToString().EndsWith("/get"))) {
|
||||||
Match m = new Regex("^zwavebot/devices/(\\d+)/(\\d+)/(\\d+)/[gs]et$|^zwavebot/devices/(\\d+)/(\\d+)/(\\d+)/(\\d+)/[gs]et$").Match(e.From.ToString());
|
Match m = new Regex("^zwavebot/devices/(\\d+)/(\\d+)/(\\d+)/[gs]et$|^zwavebot/devices/(\\d+)/(\\d+)/(\\d+)/(\\d+)/[gs]et$").Match(e.From.ToString());
|
||||||
String id = "";
|
String id = "";
|
||||||
@@ -88,6 +100,8 @@ namespace ZwayBot.Moduls {
|
|||||||
this.Update?.Invoke(this, cs.Item2);
|
this.Update?.Invoke(this, cs.Item2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ using BlubbFish.Utils.IoT.Bots;
|
|||||||
using BlubbFish.Utils.IoT.Bots.Events;
|
using BlubbFish.Utils.IoT.Bots.Events;
|
||||||
using BlubbFish.Utils.IoT.Bots.Moduls;
|
using BlubbFish.Utils.IoT.Bots.Moduls;
|
||||||
|
|
||||||
namespace ZwayBot.Moduls {
|
namespace BlubbFish.IoT.Bots.ZwayBot.Moduls {
|
||||||
internal class Overtaker : Overtaker<ZwayController> {
|
internal class Overtaker : Overtaker<ZwayController> {
|
||||||
private readonly Dictionary<String, ACommandClass> overtakeritems = new Dictionary<String, ACommandClass>();
|
private readonly Dictionary<String, ACommandClass> overtakeritems = new Dictionary<String, ACommandClass>();
|
||||||
public override event ModulEvent Update;
|
public override event ModulEvent Update;
|
||||||
@@ -19,23 +19,26 @@ namespace ZwayBot.Moduls {
|
|||||||
protected override void LibraryUpdateHook(String id) {
|
protected override void LibraryUpdateHook(String id) {
|
||||||
foreach (KeyValuePair<String, ACommandClass> item in this.overtakeritems) {
|
foreach (KeyValuePair<String, ACommandClass> item in this.overtakeritems) {
|
||||||
item.Value.Polling = false;
|
item.Value.Polling = false;
|
||||||
item.Value.Update -= this.ChangedEvent;
|
item.Value.Update -= this.HandleLibUpdate;
|
||||||
}
|
}
|
||||||
this.overtakeritems.Clear();
|
this.overtakeritems.Clear();
|
||||||
ACommandClass c = this.library.GetCommandClass(id);
|
ACommandClass c = this.library.GetCommandClass(id);
|
||||||
if (c != null) {
|
if (c != null) {
|
||||||
this.overtakeritems.Add(id, c);
|
this.overtakeritems.Add(id, c);
|
||||||
c.Polling = true;
|
c.Polling = true;
|
||||||
c.Update += this.ChangedEvent;
|
c.Update += this.HandleLibUpdate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ChangedEvent(Object sender, DeviceUpdateEvent e) {
|
protected override void LibUpadteThread(Object state) {
|
||||||
if(sender.GetType().HasAbstract(typeof(ACommandClass))) {
|
try {
|
||||||
if (this.events.ContainsKey(((ACommandClass)sender).Id)) {
|
DeviceUpdateEvent e = state as DeviceUpdateEvent;
|
||||||
this.SetValues(sender, ((ACommandClass)sender).Id, this.events[((ACommandClass)sender).Id]);
|
if (e.Parent.GetType().HasAbstract(typeof(ACommandClass))) {
|
||||||
|
if (this.events.ContainsKey(((ACommandClass)e.Parent).Id)) {
|
||||||
|
this.SetValues(e.Parent, ((ACommandClass)e.Parent).Id, this.events[((ACommandClass)e.Parent).Id]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch { }
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SetValueHook(String id, String prop, String value) {
|
protected override void SetValueHook(String id, String prop, String value) {
|
||||||
@@ -49,5 +52,7 @@ namespace ZwayBot.Moduls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void EventLibSetter() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,7 @@ using BlubbFish.Utils.IoT.Connector;
|
|||||||
using BlubbFish.Utils.IoT.Events;
|
using BlubbFish.Utils.IoT.Events;
|
||||||
using BlubbFish.Utils.IoT.Interfaces;
|
using BlubbFish.Utils.IoT.Interfaces;
|
||||||
|
|
||||||
namespace ZwayBot.Moduls {
|
namespace BlubbFish.IoT.Bots.ZwayBot.Moduls {
|
||||||
class Senml : Mqtt {
|
class Senml : Mqtt {
|
||||||
public override event ModulEvent Update;
|
public override event ModulEvent Update;
|
||||||
private readonly String SenmlGuid;
|
private readonly String SenmlGuid;
|
||||||
@@ -20,12 +20,15 @@ namespace ZwayBot.Moduls {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
protected override void EventOutput(Object sender, DeviceUpdateEvent e) {
|
protected override void LibUpadteThread(Object state) {
|
||||||
|
try {
|
||||||
|
if (this.mqttConnect) {
|
||||||
|
DeviceUpdateEvent e = state as DeviceUpdateEvent;
|
||||||
String topic = "";
|
String topic = "";
|
||||||
String data = "";
|
String data = "";
|
||||||
if (e.Parent.GetType().HasInterface(typeof(ISenml))) {
|
if (e.Parent.GetType().HasInterface(typeof(ISenml))) {
|
||||||
ISenml sensor = (ISenml)e.Parent;
|
ISenml sensor = (ISenml)e.Parent;
|
||||||
topic = sensor.SenmlTopic() + this.SenmlGuid+ "/senml";
|
topic = sensor.SenmlTopic() + this.SenmlGuid + "/senml";
|
||||||
data = sensor.ToSenml();
|
data = sensor.ToSenml();
|
||||||
}
|
}
|
||||||
if (topic != "" && data != null && data != "") {
|
if (topic != "" && data != null && data != "") {
|
||||||
@@ -33,8 +36,10 @@ namespace ZwayBot.Moduls {
|
|||||||
this.Update?.Invoke(this, new SenmlEvent(topic, data));
|
this.Update?.Invoke(this, new SenmlEvent(topic, data));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch { }
|
||||||
|
}
|
||||||
|
|
||||||
protected override void EventInput(Object sender, BackendEvent e) { }
|
protected override void MqttUpdate(Object sender, BackendEvent e) { }
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using BlubbFish.Utils;
|
|||||||
using BlubbFish.Utils.IoT.Bots.Events;
|
using BlubbFish.Utils.IoT.Bots.Events;
|
||||||
using BlubbFish.Utils.IoT.Bots.Moduls;
|
using BlubbFish.Utils.IoT.Bots.Moduls;
|
||||||
|
|
||||||
namespace ZwayBot.Moduls {
|
namespace BlubbFish.IoT.Bots.ZwayBot.Moduls {
|
||||||
class Statuspolling : Statuspolling<ZwayController> {
|
class Statuspolling : Statuspolling<ZwayController> {
|
||||||
public override event ModulEvent Update;
|
public override event ModulEvent Update;
|
||||||
|
|
||||||
@@ -37,5 +37,9 @@ namespace ZwayBot.Moduls {
|
|||||||
}
|
}
|
||||||
this.Update?.Invoke(this, new StatusPollingEvent("Polling", "all nodes"));
|
this.Update?.Invoke(this, new StatusPollingEvent("Polling", "all nodes"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void EventLibSetter() { }
|
||||||
|
|
||||||
|
protected override void LibUpadteThread(Object state) { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-7
@@ -4,8 +4,8 @@ using BlubbFish.IoT.Zway;
|
|||||||
using BlubbFish.Utils;
|
using BlubbFish.Utils;
|
||||||
using BlubbFish.Utils.IoT.Bots;
|
using BlubbFish.Utils.IoT.Bots;
|
||||||
|
|
||||||
namespace ZwayBot {
|
namespace BlubbFish.IoT.Bots.ZwayBot {
|
||||||
class Program : Bot {
|
class Program : Bot<ZwayController> {
|
||||||
static void Main(String[] args) => new Program(args);
|
static void Main(String[] args) => new Program(args);
|
||||||
|
|
||||||
public Program(String[] args) {
|
public Program(String[] args) {
|
||||||
@@ -23,15 +23,15 @@ namespace ZwayBot {
|
|||||||
this.logger.SetPath(InIReader.GetInstance("settings").GetValue("logging", "path"));
|
this.logger.SetPath(InIReader.GetInstance("settings").GetValue("logging", "path"));
|
||||||
ZwayController zw = new ZwayController(InIReader.GetInstance("settings").GetSection("zway"), names, false);
|
ZwayController zw = new ZwayController(InIReader.GetInstance("settings").GetSection("zway"), names, false);
|
||||||
zw.Update += this.ZwayDataUpate;
|
zw.Update += this.ZwayDataUpate;
|
||||||
this.ModulLoader<ZwayController>("ZwayBot.Moduls", zw);
|
this.ModulLoader("BlubbFish.IoT.Bots.ZwayBot.Moduls", zw);
|
||||||
this.ModulInterconnect<ZwayController>();
|
this.ModulInterconnect();
|
||||||
this.ModulEvents<ZwayController>();
|
this.ModulEvents();
|
||||||
this.WaitForShutdown();
|
this.WaitForShutdown();
|
||||||
this.ModulDispose<ZwayController>();
|
this.ModulDispose();
|
||||||
zw.Dispose();
|
zw.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ZwayDataUpate(Object sender, BlubbFish.IoT.Zway.Events.DeviceUpdateEvent e) {
|
private void ZwayDataUpate(Object sender, Zway.Events.DeviceUpdateEvent e) {
|
||||||
Console.WriteLine("-> ZW [" + e.UpdateTime + "]: " + e.Parent.ToString());
|
Console.WriteLine("-> ZW [" + e.UpdateTime + "]: " + e.Parent.ToString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ using System.Runtime.InteropServices;
|
|||||||
[assembly: AssemblyConfiguration("")]
|
[assembly: AssemblyConfiguration("")]
|
||||||
[assembly: AssemblyCompany("BlubbFish")]
|
[assembly: AssemblyCompany("BlubbFish")]
|
||||||
[assembly: AssemblyProduct("Zway-Bot")]
|
[assembly: AssemblyProduct("Zway-Bot")]
|
||||||
[assembly: AssemblyCopyright("Copyright © 2017 - 05.06.2018")]
|
[assembly: AssemblyCopyright("Copyright © 2017 - 29.09.2018")]
|
||||||
[assembly: AssemblyTrademark("BlubbFish")]
|
[assembly: AssemblyTrademark("BlubbFish")]
|
||||||
[assembly: AssemblyCulture("")]
|
[assembly: AssemblyCulture("")]
|
||||||
|
|
||||||
@@ -31,8 +31,8 @@ using System.Runtime.InteropServices;
|
|||||||
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
|
||||||
// übernehmen, indem Sie "*" eingeben:
|
// übernehmen, indem Sie "*" eingeben:
|
||||||
// [assembly: AssemblyVersion("1.0.*")]
|
// [assembly: AssemblyVersion("1.0.*")]
|
||||||
[assembly: AssemblyVersion("1.6.2")]
|
[assembly: AssemblyVersion("1.7.0")]
|
||||||
[assembly: AssemblyFileVersion("1.6.2")]
|
[assembly: AssemblyFileVersion("1.7.0")]
|
||||||
[assembly: NeutralResourcesLanguage("de-DE")]
|
[assembly: NeutralResourcesLanguage("de-DE")]
|
||||||
|
|
||||||
// “Internet Of Things” icon by By Michael Wohlwend, US, from thenounproject.com.
|
// “Internet Of Things” icon by By Michael Wohlwend, US, from thenounproject.com.
|
||||||
|
|||||||
+2
-2
@@ -8,7 +8,7 @@
|
|||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace ZwayBot.Properties {
|
namespace BlubbFish.IoT.Bots.ZwayBot.Properties {
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ namespace ZwayBot.Properties {
|
|||||||
public static global::System.Resources.ResourceManager ResourceManager {
|
public static global::System.Resources.ResourceManager ResourceManager {
|
||||||
get {
|
get {
|
||||||
if (object.ReferenceEquals(resourceMan, null)) {
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ZwayBot.Properties.Resources", typeof(Resources).Assembly);
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("BlubbFish.IoT.Bots.ZwayBot.Properties.Resources", typeof(Resources).Assembly);
|
||||||
resourceMan = temp;
|
resourceMan = temp;
|
||||||
}
|
}
|
||||||
return resourceMan;
|
return resourceMan;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||||
<ProjectGuid>{AEF8059F-8AEF-45C7-85C4-318C8F797900}</ProjectGuid>
|
<ProjectGuid>{AEF8059F-8AEF-45C7-85C4-318C8F797900}</ProjectGuid>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<RootNamespace>ZwayBot</RootNamespace>
|
<RootNamespace>BlubbFish.IoT.Bots.ZwayBot</RootNamespace>
|
||||||
<AssemblyName>Zway-Bot</AssemblyName>
|
<AssemblyName>Zway-Bot</AssemblyName>
|
||||||
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
|
<TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
|
||||||
<FileAlignment>512</FileAlignment>
|
<FileAlignment>512</FileAlignment>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<StartupObject>ZwayBot.Program</StartupObject>
|
<StartupObject>BlubbFish.IoT.Bots.ZwayBot.Program</StartupObject>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ApplicationIcon>Resources\Icon.ico</ApplicationIcon>
|
<ApplicationIcon>Resources\Icon.ico</ApplicationIcon>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -6,7 +6,7 @@ OUTPUT="../bin/Release"
|
|||||||
|
|
||||||
EXEC="$ROOT/usr/local/bin/zwaybot"
|
EXEC="$ROOT/usr/local/bin/zwaybot"
|
||||||
CONFIG="$ROOT/etc/zwaybot"
|
CONFIG="$ROOT/etc/zwaybot"
|
||||||
SYSTEMD="$ROOT/etc/systemd/system"
|
SYSTEMD="$ROOT/lib/systemd/system"
|
||||||
LOGROTATE="$ROOT/etc/logrotate.d"
|
LOGROTATE="$ROOT/etc/logrotate.d"
|
||||||
|
|
||||||
DEBIAN="$ROOT/DEBIAN"
|
DEBIAN="$ROOT/DEBIAN"
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
systemctl enable zwaybot
|
||||||
systemctl daemon-reload
|
systemctl daemon-reload
|
||||||
if [[ $(systemctl is-active zwaybot || true) == "active" ]]
|
if [[ $(systemctl is-active zwaybot || true) == "active" ]]
|
||||||
then
|
then
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ WorkingDirectory=/usr/local/bin/zwaybot
|
|||||||
# ExecStartPre=/bin/rm /var/log/zwaybot.log && /bin/touch /var/log/zwaybot.log && /bin/chown zwaybot:zwaybot /var/log/zwaybot.log && /bin/chmod 644 /var/log/zwaybot.log
|
# ExecStartPre=/bin/rm /var/log/zwaybot.log && /bin/touch /var/log/zwaybot.log && /bin/chown zwaybot:zwaybot /var/log/zwaybot.log && /bin/chmod 644 /var/log/zwaybot.log
|
||||||
ExecStart=/usr/bin/mono /usr/local/bin/zwaybot/Zway-Bot.exe
|
ExecStart=/usr/bin/mono /usr/local/bin/zwaybot/Zway-Bot.exe
|
||||||
KillMode=control-group
|
KillMode=control-group
|
||||||
|
TimeoutStopSec=5
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
StandardOutput=null
|
StandardOutput=null
|
||||||
StandardError=syslog
|
StandardError=syslog
|
||||||
|
|||||||
Reference in New Issue
Block a user