Initial commit
This commit is contained in:
commit
4f74caac6f
|
@ -0,0 +1,399 @@
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.rsuser
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated project file (contains which files were open etc.)
|
||||||
|
*.vbp
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
*.code-workspace
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
*.sln.iml
|
||||||
|
*.db
|
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||||
|
// Use hover for the description of the existing attributes
|
||||||
|
// For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
|
||||||
|
"name": ".NET Core Launch (console)",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "launch",
|
||||||
|
"preLaunchTask": "build",
|
||||||
|
// If you have changed target frameworks, make sure to update the program path.
|
||||||
|
"program": "${workspaceFolder}/ChatrClient/Chatr.Skia.Gtk/bin/Debug/net6.0/Chatr.Skia.Gtk.dll",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}/ChatrClient/Chatr.Skia.Gtk",
|
||||||
|
// For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
|
||||||
|
"console": "internalConsole",
|
||||||
|
"stopAtEntry": false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": ".NET Core Attach",
|
||||||
|
"type": "coreclr",
|
||||||
|
"request": "attach"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"label": "build",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"build",
|
||||||
|
"${workspaceFolder}/ChatrClient/Chatr.Skia.Gtk/Chatr.Skia.Gtk.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "publish",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"publish",
|
||||||
|
"${workspaceFolder}/ChatrClient/Chatr.Skia.Gtk/Chatr.Skia.Gtk.csproj",
|
||||||
|
"/property:GenerateFullPaths=true",
|
||||||
|
"/consoleloggerparameters:NoSummary"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "watch",
|
||||||
|
"command": "dotnet",
|
||||||
|
"type": "process",
|
||||||
|
"args": [
|
||||||
|
"watch",
|
||||||
|
"run",
|
||||||
|
"--project",
|
||||||
|
"${workspaceFolder}/ChatrClient/Chatr.Skia.Gtk/Chatr.Skia.Gtk.csproj"
|
||||||
|
],
|
||||||
|
"problemMatcher": "$msCompile"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||||
|
<PackageReference Include="SIPSorcery.WebSocketSharp" Version="0.0.1" />
|
||||||
|
<PackageReference Include="System.Net.Http" Version="4.3.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,347 @@
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
|
||||||
|
namespace Chatr
|
||||||
|
{
|
||||||
|
internal class appcfg
|
||||||
|
{
|
||||||
|
public bool WS_SAME_AS_HTTP {get;set;}
|
||||||
|
public ushort WSPort {get;set;}
|
||||||
|
public bool WSS {get;set;}
|
||||||
|
|
||||||
|
}
|
||||||
|
public class State
|
||||||
|
{
|
||||||
|
|
||||||
|
public string Email {get;set;}
|
||||||
|
public string Name {get;set;}
|
||||||
|
public string Url {get;set;}
|
||||||
|
public bool Loggedin {get;set;}
|
||||||
|
|
||||||
|
public List<MyMessage> Mymessages {get;set;}
|
||||||
|
|
||||||
|
public List<UserAccount> Users {get;set;}
|
||||||
|
}
|
||||||
|
public class ChatrWSMsg
|
||||||
|
{
|
||||||
|
public ChatrWSMsg()
|
||||||
|
{
|
||||||
|
PacketType=PacketType.NoOp;
|
||||||
|
|
||||||
|
}
|
||||||
|
public ChatrWSMsg(IPacketData data)
|
||||||
|
{
|
||||||
|
SetPacketData(data);
|
||||||
|
}
|
||||||
|
public PacketType PacketType {get;set;}
|
||||||
|
public JObject Data {get;set;}
|
||||||
|
|
||||||
|
public IPacketData GetPacketData()
|
||||||
|
{
|
||||||
|
if(Data == null) return new NoOpPacket();
|
||||||
|
switch(PacketType)
|
||||||
|
{
|
||||||
|
case PacketType.AuthenticateUser:
|
||||||
|
return Data.ToObject<AuthenticationUserPacket>();
|
||||||
|
case PacketType.AuthenticateBot:
|
||||||
|
return Data.ToObject<AuthenticateBotPacket>();
|
||||||
|
case PacketType.Message:
|
||||||
|
return Data.ToObject<MessagePacket>();
|
||||||
|
case PacketType.Notification:
|
||||||
|
return Data.ToObject<NotificationPacket>();
|
||||||
|
case PacketType.NotificationEventFire:
|
||||||
|
return Data.ToObject<NotificationEventFirePacket>();
|
||||||
|
case PacketType.ClearAll:
|
||||||
|
return Data.ToObject<ClearAllPacket>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return new NoOpPacket();
|
||||||
|
}
|
||||||
|
public void SetPacketData(IPacketData data)
|
||||||
|
{
|
||||||
|
PacketType=data.PacketType;
|
||||||
|
Data=JObject.FromObject(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsType(PacketType type,out IPacketData data)
|
||||||
|
{
|
||||||
|
|
||||||
|
var res= type == PacketType;
|
||||||
|
if(res)
|
||||||
|
{
|
||||||
|
var gata=GetPacketData();
|
||||||
|
if(gata != null)
|
||||||
|
{
|
||||||
|
data=gata;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
data=new NoOpPacket();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class IPacketData
|
||||||
|
{
|
||||||
|
|
||||||
|
public abstract PacketType PacketType {get;}
|
||||||
|
}
|
||||||
|
public class NoOpPacket : IPacketData
|
||||||
|
{
|
||||||
|
public override PacketType PacketType => PacketType.NoOp;
|
||||||
|
}
|
||||||
|
public class AuthenticationUserPacket : IPacketData
|
||||||
|
{
|
||||||
|
public AuthenticationUserPacket()
|
||||||
|
{
|
||||||
|
SessionId="";
|
||||||
|
}
|
||||||
|
[JsonIgnore]
|
||||||
|
public override PacketType PacketType => PacketType.AuthenticateUser;
|
||||||
|
|
||||||
|
public string SessionId {get;set;}
|
||||||
|
}
|
||||||
|
public class AuthenticateBotPacket : IPacketData
|
||||||
|
{
|
||||||
|
public AuthenticateBotPacket()
|
||||||
|
{
|
||||||
|
ApiKey="";
|
||||||
|
}
|
||||||
|
[JsonIgnore]
|
||||||
|
public override PacketType PacketType => PacketType.AuthenticateBot;
|
||||||
|
|
||||||
|
|
||||||
|
public string ApiKey {get;set;}
|
||||||
|
}
|
||||||
|
public class ClearAllPacket : IPacketData
|
||||||
|
{
|
||||||
|
public override PacketType PacketType => PacketType.ClearAll;
|
||||||
|
|
||||||
|
public string UserStr {get;set;}
|
||||||
|
}
|
||||||
|
public class MessagePacket : IPacketData
|
||||||
|
{
|
||||||
|
public override PacketType PacketType => PacketType.Message;
|
||||||
|
|
||||||
|
public string Name {get;set;}
|
||||||
|
public string Content {get;set;}
|
||||||
|
public string CreationTime {get;set;}
|
||||||
|
public string UserStr {get;set;}
|
||||||
|
}
|
||||||
|
public enum PacketType
|
||||||
|
{
|
||||||
|
NoOp=0,
|
||||||
|
AuthenticateUser=1,
|
||||||
|
AuthenticateBot=2,
|
||||||
|
Message=3,
|
||||||
|
|
||||||
|
Notification=4,
|
||||||
|
|
||||||
|
NotificationEventFire=5,
|
||||||
|
ClearAll= 6
|
||||||
|
|
||||||
|
}
|
||||||
|
public class NotificationEventFirePacket : IPacketData
|
||||||
|
{
|
||||||
|
public string DestinationUserName {get;set;}
|
||||||
|
public string SourceUserName {get;set;}
|
||||||
|
public string SourceBotUserName {get;set;}
|
||||||
|
public string BotNotificationId {get;set;}
|
||||||
|
public string Button {get;set;}
|
||||||
|
public string BotUserString {get;set;}
|
||||||
|
public override PacketType PacketType => PacketType.NotificationEventFire;
|
||||||
|
}
|
||||||
|
public class NotificationPacket : IPacketData
|
||||||
|
{
|
||||||
|
public NotificationPacket()
|
||||||
|
{
|
||||||
|
OnClick=new NotificationEvent();
|
||||||
|
NotificationButtons=new List<NotificationButton>();
|
||||||
|
}
|
||||||
|
public override PacketType PacketType => PacketType.Notification;
|
||||||
|
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string BotNotificationId {get;set;} //an id chosen by bot
|
||||||
|
public string BotOwnerName {get;set;} //cleartext name of bot owner
|
||||||
|
public string BotOwnerUserName {get;set;} //username of bot owner
|
||||||
|
public string Title {get;set;}
|
||||||
|
|
||||||
|
public string Body {get;set;}
|
||||||
|
|
||||||
|
public string BotName {get;set;}
|
||||||
|
public string BotUserString {get;set;}
|
||||||
|
|
||||||
|
public string UserName {get;set;}
|
||||||
|
|
||||||
|
public NotificationEvent OnClick {get;set;}
|
||||||
|
|
||||||
|
public List<NotificationButton> NotificationButtons {get;set;}
|
||||||
|
|
||||||
|
}
|
||||||
|
public class NotificationButton
|
||||||
|
{
|
||||||
|
public NotificationButton()
|
||||||
|
{
|
||||||
|
Text="";
|
||||||
|
Event=new NotificationEvent();
|
||||||
|
}
|
||||||
|
public string Text {get;set;}
|
||||||
|
public NotificationEvent Event {get;set;}
|
||||||
|
|
||||||
|
public static NotificationButton CreateButton(string text,NotificationEvent evt)
|
||||||
|
{
|
||||||
|
NotificationButton btn=new NotificationButton();
|
||||||
|
btn.Text=text;
|
||||||
|
btn.Event = evt;
|
||||||
|
return btn;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum NotificationEventType
|
||||||
|
{
|
||||||
|
DoNothing=0,
|
||||||
|
DownloadOnClient=1,
|
||||||
|
GoToOnClient=2,
|
||||||
|
CallApiFromServer=3, //is the only one that device has to send for
|
||||||
|
SendCallbackToBot=4 //ws only
|
||||||
|
}
|
||||||
|
public class NotificationEvent
|
||||||
|
{
|
||||||
|
public NotificationEvent()
|
||||||
|
{
|
||||||
|
Type=NotificationEventType.DoNothing;
|
||||||
|
Data=new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NotificationEventType Type {get;set;}
|
||||||
|
|
||||||
|
public Dictionary<string,string> Data {get;set;}
|
||||||
|
|
||||||
|
public static NotificationEvent CreateRedirect(string url)
|
||||||
|
{
|
||||||
|
NotificationEvent evt=new NotificationEvent();
|
||||||
|
evt.Type = NotificationEventType.GoToOnClient;
|
||||||
|
evt.Data.Add("Url",url);
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
public static NotificationEvent CreateDownload(string url)
|
||||||
|
{
|
||||||
|
NotificationEvent evt=new NotificationEvent();
|
||||||
|
evt.Type = NotificationEventType.DownloadOnClient;
|
||||||
|
evt.Data.Add("Url",url);
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
public static NotificationEvent CreateBotCallback()
|
||||||
|
{
|
||||||
|
NotificationEvent evt=new NotificationEvent();
|
||||||
|
evt.Type=NotificationEventType.SendCallbackToBot;
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NotificationHttpBuilder CreateServerCallback(HttpMethod method,string url)
|
||||||
|
{
|
||||||
|
NotificationHttpBuilder builder=new NotificationHttpBuilder(method,url);
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class NotificationHttpBuilder
|
||||||
|
{
|
||||||
|
internal NotificationHttpBuilder(HttpMethod method,string url)
|
||||||
|
{
|
||||||
|
this.method = method.Method;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
string method;
|
||||||
|
string url;
|
||||||
|
Dictionary<string,List<string>> headers=new Dictionary<string, List<string>>();
|
||||||
|
|
||||||
|
byte[] body=new byte[0];
|
||||||
|
private void AddTo(Dictionary<string,List<string>> ls,string key,string value)
|
||||||
|
{
|
||||||
|
if(!ls.ContainsKey(key))
|
||||||
|
{
|
||||||
|
ls.Add(key,new List<string>());
|
||||||
|
}
|
||||||
|
ls[key].Add(value);
|
||||||
|
}
|
||||||
|
public NotificationHttpBuilder AddHeader(string key,string value)
|
||||||
|
{
|
||||||
|
AddTo(headers,key,value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public NotificationHttpBuilder SetBody(string data,Encoding encoding)
|
||||||
|
{
|
||||||
|
body=encoding.GetBytes(data);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public NotificationHttpBuilder SetBody(string data)
|
||||||
|
{
|
||||||
|
return SetBody(data,Encoding.UTF8);
|
||||||
|
}
|
||||||
|
public NotificationHttpBuilder SetBody(byte[] data)
|
||||||
|
{
|
||||||
|
body = data;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public NotificationEvent Build()
|
||||||
|
{
|
||||||
|
NotificationEvent evt=new NotificationEvent();
|
||||||
|
evt.Type=NotificationEventType.CallApiFromServer;
|
||||||
|
foreach(var header in headers)
|
||||||
|
{
|
||||||
|
int i=0;
|
||||||
|
foreach(var val in header.Value)
|
||||||
|
{
|
||||||
|
evt.Data.Add($"HDR{header.Key}:{i++}",val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
evt.Data.Add("URL",url);
|
||||||
|
evt.Data.Add("MET",method);
|
||||||
|
evt.Data.Add("BDY",Convert.ToBase64String(body));
|
||||||
|
return evt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UserAccount
|
||||||
|
{
|
||||||
|
public UserAccount()
|
||||||
|
{
|
||||||
|
Messages=new List<MyMessage>();
|
||||||
|
Hash="";
|
||||||
|
Text="";
|
||||||
|
Shown="";
|
||||||
|
}
|
||||||
|
public string Shown {get;set;}
|
||||||
|
public string Hash {get;set;}
|
||||||
|
public string Text {get;set;}
|
||||||
|
|
||||||
|
[JsonIgnore]
|
||||||
|
public long UId {get;set;}
|
||||||
|
|
||||||
|
public List<MyMessage> Messages {get;set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class MyMessage
|
||||||
|
{
|
||||||
|
public MyMessage()
|
||||||
|
{
|
||||||
|
Body = "";
|
||||||
|
Time="";
|
||||||
|
}
|
||||||
|
public string Time {get;set;}
|
||||||
|
public bool Mine {get;set;}
|
||||||
|
public string Body {get;set;}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,246 @@
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using WebSocketSharp;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Chatr
|
||||||
|
{
|
||||||
|
public class ReceivedEventArgs<T> : EventArgs
|
||||||
|
{
|
||||||
|
public ReceivedEventArgs(T data)
|
||||||
|
{
|
||||||
|
Data=data;
|
||||||
|
}
|
||||||
|
public T Data {get;set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class WSClient : IDisposable
|
||||||
|
{
|
||||||
|
public class ChatrWSMsgReceivedEventArgs : EventArgs
|
||||||
|
{
|
||||||
|
public ChatrWSMsgReceivedEventArgs(ChatrWSMsg msg){
|
||||||
|
Message=msg;
|
||||||
|
}
|
||||||
|
public ChatrWSMsg Message {get;set;}
|
||||||
|
}
|
||||||
|
public event EventHandler<ChatrWSMsgReceivedEventArgs> OnMessage;
|
||||||
|
public event EventHandler<ErrorEventArgs> OnError;
|
||||||
|
|
||||||
|
public event EventHandler<EventArgs> OnOpen;
|
||||||
|
|
||||||
|
public event EventHandler<CloseEventArgs> OnClose;
|
||||||
|
WebSocket sock;
|
||||||
|
public WSClient(string url,params string[] protocols)
|
||||||
|
{
|
||||||
|
sock=new WebSocket(url,protocols);
|
||||||
|
sock.OnMessage += (sender,e)=>{
|
||||||
|
|
||||||
|
if(e.IsText)
|
||||||
|
{
|
||||||
|
OnMessage?.Invoke(this,new ChatrWSMsgReceivedEventArgs(JsonConvert.DeserializeObject<ChatrWSMsg>(e.Data)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sock.OnError += (sender,e)=>{
|
||||||
|
OnError?.Invoke(this,e);
|
||||||
|
};
|
||||||
|
sock.OnOpen+= (sender,e)=>{
|
||||||
|
OnOpen?.Invoke(this,e);
|
||||||
|
};
|
||||||
|
sock.OnClose += (sender,e)=>{
|
||||||
|
OnClose?.Invoke(this,e);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
public void Connect()
|
||||||
|
{
|
||||||
|
sock.Connect();
|
||||||
|
}
|
||||||
|
public void Send(ChatrWSMsg msg)
|
||||||
|
{
|
||||||
|
sock.Send(JsonConvert.SerializeObject(msg));
|
||||||
|
}
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
sock.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ChatrClient
|
||||||
|
{
|
||||||
|
|
||||||
|
public ChatrClient()
|
||||||
|
{
|
||||||
|
client=new HttpClient();
|
||||||
|
}
|
||||||
|
HttpClient client;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<string> GetSessionAsync(string url,string username,string password)
|
||||||
|
{
|
||||||
|
HttpRequestMessage msg=new HttpRequestMessage(HttpMethod.Post,$"{url.TrimEnd('/')}/api/login2");
|
||||||
|
Dictionary<string,string> ls=new Dictionary<string, string>();
|
||||||
|
ls.Add("username",username);
|
||||||
|
ls.Add("password",password);
|
||||||
|
var content=new FormUrlEncodedContent(ls);
|
||||||
|
|
||||||
|
msg.Content=content;
|
||||||
|
using(var resp=await client.SendAsync(msg)){
|
||||||
|
|
||||||
|
if(resp.IsSuccessStatusCode) {
|
||||||
|
var val= await resp.Content.ReadAsStringAsync();
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
public ChatrInstance AuthenticateUser(string endpoint,string sessionId)
|
||||||
|
{
|
||||||
|
return new ChatrInstance(client,endpoint,false,sessionId);
|
||||||
|
}
|
||||||
|
public ChatrInstance AuthenticateBot(string endpoint,string apiKey)
|
||||||
|
{
|
||||||
|
return new ChatrInstance(client,endpoint,true,apiKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public class ChatrInstance
|
||||||
|
{
|
||||||
|
public event EventHandler<ReceivedEventArgs<MessagePacket>> OnMessage;
|
||||||
|
|
||||||
|
public event EventHandler<ReceivedEventArgs<NotificationPacket>> OnNotification;
|
||||||
|
|
||||||
|
public event EventHandler<ReceivedEventArgs<NotificationEventFirePacket>> OnNotificationEventFire;
|
||||||
|
HttpClient clt;
|
||||||
|
string endpoint;
|
||||||
|
bool isBot;
|
||||||
|
string auth;
|
||||||
|
|
||||||
|
WSClient wsClt;
|
||||||
|
public async Task<State> GetStateAsync()
|
||||||
|
{
|
||||||
|
if(isBot) return new State();
|
||||||
|
|
||||||
|
HttpRequestMessage msg=new HttpRequestMessage(HttpMethod.Get,$"{endpoint.TrimEnd('/')}/api/state.json");
|
||||||
|
msg.Headers.Add("Cookie",$"Chatr-Session={auth}");
|
||||||
|
using(var resp=await clt.SendAsync(msg)){
|
||||||
|
if(resp.IsSuccessStatusCode) {
|
||||||
|
var val= await resp.Content.ReadAsStringAsync();
|
||||||
|
return JsonConvert.DeserializeObject<State>(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new State();
|
||||||
|
|
||||||
|
}
|
||||||
|
internal ChatrInstance(HttpClient clt,string endpoint,bool isBot,string auth)
|
||||||
|
{
|
||||||
|
this.clt=clt;
|
||||||
|
this.endpoint = endpoint.TrimEnd('/');
|
||||||
|
this.isBot=isBot;
|
||||||
|
this.auth = auth;
|
||||||
|
}
|
||||||
|
public async Task StartCommunication()
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
//
|
||||||
|
if(e.WS_SAME_AS_HTTP)
|
||||||
|
{
|
||||||
|
var scheme= window.location.protocol == "http" ? "ws" : "wss";
|
||||||
|
wsStr=`${scheme}://${window.location.host}${window.location.pathname}chatr-ws`;
|
||||||
|
}else{
|
||||||
|
var scheme = e.WSS ? 'wss' : 'ws';
|
||||||
|
wsStr=`${scheme}://${window.location.hostname}:${e.WSPort}${window.location.pathname}chatr-ws`;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
//
|
||||||
|
string data=await clt.GetStringAsync($"{endpoint}/api/appconfig.json");
|
||||||
|
var res=JsonConvert.DeserializeObject<appcfg>(data);
|
||||||
|
string wsURL="";
|
||||||
|
var uri=new Uri(endpoint + "/");
|
||||||
|
|
||||||
|
if(res.WS_SAME_AS_HTTP)
|
||||||
|
{
|
||||||
|
var scheme = uri.Scheme == "http" ? "ws" : "wss";
|
||||||
|
wsURL = $"{scheme}://{uri.Host}:{uri.Port}{uri.PathAndQuery.TrimEnd('/')}/chatr-ws";
|
||||||
|
|
||||||
|
}else{
|
||||||
|
var scheme = res.WSS ? "wss" : "ws";
|
||||||
|
wsURL = $"{scheme}://{uri.Host}:{res.WSPort}{uri.PathAndQuery.TrimEnd('/')}/chatr-ws";
|
||||||
|
}
|
||||||
|
wsClt=new WSClient(wsURL);
|
||||||
|
var authPkt=new ChatrWSMsg();
|
||||||
|
if(isBot)
|
||||||
|
{
|
||||||
|
AuthenticateBotPacket botPacket=new AuthenticateBotPacket();
|
||||||
|
botPacket.ApiKey = auth;
|
||||||
|
authPkt.SetPacketData(botPacket);
|
||||||
|
|
||||||
|
|
||||||
|
}else{
|
||||||
|
AuthenticationUserPacket userPkt=new AuthenticationUserPacket();
|
||||||
|
userPkt.SessionId = auth;
|
||||||
|
authPkt.SetPacketData(userPkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
wsClt.OnMessage += (sender,e)=>{
|
||||||
|
IPacketData packetData;
|
||||||
|
|
||||||
|
if(e.Message.IsType(PacketType.Message,out packetData))
|
||||||
|
{
|
||||||
|
var message=(MessagePacket)packetData;
|
||||||
|
OnMessage?.Invoke(this,new ReceivedEventArgs<MessagePacket>(message));
|
||||||
|
}
|
||||||
|
if(e.Message.IsType(PacketType.Notification,out packetData))
|
||||||
|
{
|
||||||
|
var notification =(NotificationPacket)packetData;
|
||||||
|
OnNotification?.Invoke(this,new ReceivedEventArgs<NotificationPacket>(notification));
|
||||||
|
}
|
||||||
|
if(e.Message.IsType(PacketType.NotificationEventFire,out packetData))
|
||||||
|
{
|
||||||
|
var noFire = (NotificationEventFirePacket)packetData;
|
||||||
|
OnNotificationEventFire?.Invoke(this,new ReceivedEventArgs<NotificationEventFirePacket>(noFire));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
wsClt.Connect();
|
||||||
|
wsClt.Send(authPkt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SendMessage(string userName,string messageData)
|
||||||
|
{
|
||||||
|
MessagePacket pkt=new MessagePacket();
|
||||||
|
|
||||||
|
pkt.Content = messageData;
|
||||||
|
pkt.UserStr=userName;
|
||||||
|
wsClt.Send(new ChatrWSMsg(pkt));
|
||||||
|
}
|
||||||
|
public void SendNotification(string userName,string botNotId,string title,string body,NotificationEvent clicked,params NotificationButton[] links)
|
||||||
|
{
|
||||||
|
NotificationPacket pkt=new NotificationPacket();
|
||||||
|
pkt.UserName=userName;
|
||||||
|
pkt.BotNotificationId = botNotId;
|
||||||
|
pkt.Title = title;
|
||||||
|
pkt.Body= body;
|
||||||
|
pkt.OnClick = clicked;
|
||||||
|
pkt.NotificationButtons=links.ToList();
|
||||||
|
wsClt.Send(new ChatrWSMsg(pkt));
|
||||||
|
}
|
||||||
|
public void Click(NotificationPacket pkt,string eventFired="")
|
||||||
|
{
|
||||||
|
NotificationEventFirePacket efp=new NotificationEventFirePacket();
|
||||||
|
efp.BotUserString=pkt.BotUserString;
|
||||||
|
efp.BotNotificationId=pkt.BotNotificationId;
|
||||||
|
efp.SourceUserName = pkt.BotOwnerUserName;
|
||||||
|
efp.Button=eventFired;
|
||||||
|
wsClt.Send(new ChatrWSMsg(efp));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\ChatrCommon\ChatrCommon.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,33 @@
|
||||||
|
using Chatr;
|
||||||
|
|
||||||
|
ChatrClient clt=new ChatrClient();
|
||||||
|
var bot=clt.AuthenticateBot("http://127.0.0.1:4031/","YOUR_API_KEY");
|
||||||
|
bot.OnMessage += (sender,e)=>{
|
||||||
|
|
||||||
|
Console.WriteLine($"New Messsage: {e.Data.Content}");
|
||||||
|
};
|
||||||
|
await bot.StartCommunication();
|
||||||
|
|
||||||
|
bot.SendMessage("me","Demi Lovato Is Cool");
|
||||||
|
|
||||||
|
bot.SendNotification("mnolan1202","SOME_NOTIFICATION223","Hot Chicks?","Do You Want Demi Lovato?",
|
||||||
|
NotificationEvent.CreateBotCallback(),
|
||||||
|
NotificationButton.CreateButton("Download",NotificationEvent.CreateDownload("http://192.168.0.142:3252/api/Storage/Video/PzUKeGZiEl0")),
|
||||||
|
NotificationButton.CreateButton("Fire Bot",NotificationEvent.CreateBotCallback())
|
||||||
|
);
|
||||||
|
|
||||||
|
bot.OnNotificationEventFire += (sender,e)=>{
|
||||||
|
|
||||||
|
if(string.IsNullOrWhiteSpace(e.Data.Button))
|
||||||
|
{
|
||||||
|
Console.WriteLine($"Notification: \"{e.Data.BotNotificationId}\" Clicked");
|
||||||
|
}else{
|
||||||
|
Console.WriteLine($"Notification: \"{e.Data.BotNotificationId}\" Action \"{e.Data.Button}\" Clicked");
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
while(true)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,2 @@
|
||||||
|
// See https://aka.ms/new-console-template for more information
|
||||||
|
Console.WriteLine("Hello, World!");
|
|
@ -0,0 +1,13 @@
|
||||||
|
/api/sendmsg
|
||||||
|
{
|
||||||
|
"ApiKey":"SomeApiKey",
|
||||||
|
"Title":"This Is A Notification",
|
||||||
|
"Body":"This is a body"
|
||||||
|
"To":{
|
||||||
|
"Username":"username@example.com",
|
||||||
|
"AllDevices":false,
|
||||||
|
"Device":"MyMainPhone"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/api/messages/count
|
||||||
|
/api/messages/
|
|
@ -0,0 +1,167 @@
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class Account
|
||||||
|
{
|
||||||
|
public Account()
|
||||||
|
{
|
||||||
|
Name="";
|
||||||
|
Username="";
|
||||||
|
HashedPassword="";
|
||||||
|
Salt="";
|
||||||
|
Email="";
|
||||||
|
ShowUserNameToAnyBody=false;
|
||||||
|
ShowBots=true;
|
||||||
|
HiddenBots=new List<long>();
|
||||||
|
HiddenUsers=new List<long>();
|
||||||
|
IsAnonymous=false;
|
||||||
|
IsAdmin=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsHidden(long user,long bot)
|
||||||
|
{
|
||||||
|
if(bot != -1 && !ShowBots) return true;
|
||||||
|
|
||||||
|
if(HiddenUsers.Contains(user)) return true;
|
||||||
|
|
||||||
|
if(bot == -1) return false;
|
||||||
|
if(HiddenBots.Contains(bot)) return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public bool IsAnonymous {get;set;}
|
||||||
|
public long Id {get;set;}
|
||||||
|
public string Name {get;set;}
|
||||||
|
public string Username {get;set;}
|
||||||
|
|
||||||
|
public string Email {get;set;}
|
||||||
|
|
||||||
|
public string HashedPassword {get;set;}
|
||||||
|
|
||||||
|
public string Salt {get;set;}
|
||||||
|
|
||||||
|
public bool Verified {get;set;}
|
||||||
|
|
||||||
|
public bool ShowUserNameToAnyBody {get;set;}
|
||||||
|
|
||||||
|
public bool ShowBots {get;set;}
|
||||||
|
|
||||||
|
public List<long> HiddenUsers {get;set;}
|
||||||
|
|
||||||
|
public List<long> HiddenBots {get;set;}
|
||||||
|
public bool IsAdmin { get; internal set; }
|
||||||
|
|
||||||
|
public bool IsValid()
|
||||||
|
{
|
||||||
|
if(!IsValidEmail(Email)) return false;
|
||||||
|
if(!IsValidUserName(Username)) return false;
|
||||||
|
if(!IsValidName(Name)) return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static bool IsValidName(string name)
|
||||||
|
{
|
||||||
|
foreach(var c in name)
|
||||||
|
{
|
||||||
|
if(c == ' ') continue;
|
||||||
|
if(char.IsLetterOrDigit(c)) continue;
|
||||||
|
if(c=='_') continue;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static bool IsValidUserName(string username)
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(username)) return false;
|
||||||
|
if(username.Contains(" ")) return false;
|
||||||
|
|
||||||
|
return IsValidName(username);
|
||||||
|
}
|
||||||
|
private static bool IsValidEmail(string email)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(email))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Normalize the domain
|
||||||
|
email = Regex.Replace(email, @"(@)(.+)$", DomainMapper,
|
||||||
|
RegexOptions.None, TimeSpan.FromMilliseconds(200));
|
||||||
|
|
||||||
|
// Examines the domain part of the email and normalizes it.
|
||||||
|
string DomainMapper(Match match)
|
||||||
|
{
|
||||||
|
// Use IdnMapping class to convert Unicode domain names.
|
||||||
|
var idn = new IdnMapping();
|
||||||
|
|
||||||
|
// Pull out and process domain name (throws ArgumentException on invalid)
|
||||||
|
string domainName = idn.GetAscii(match.Groups[2].Value);
|
||||||
|
|
||||||
|
return match.Groups[1].Value + domainName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (RegexMatchTimeoutException e)
|
||||||
|
{
|
||||||
|
_=e;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (ArgumentException e)
|
||||||
|
{
|
||||||
|
_=e;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return Regex.IsMatch(email,
|
||||||
|
@"^[^@\s]+@[^@\s]+\.[^@\s]+$",
|
||||||
|
RegexOptions.IgnoreCase, TimeSpan.FromMilliseconds(250));
|
||||||
|
}
|
||||||
|
catch (RegexMatchTimeoutException)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public bool IsCorrectPassword(string password)
|
||||||
|
{
|
||||||
|
string pass = $"{password}{Salt}";
|
||||||
|
|
||||||
|
using(SHA256 mySHA256 = SHA256.Create())
|
||||||
|
{
|
||||||
|
string check= Convert.ToBase64String(mySHA256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(pass)));
|
||||||
|
return check.Equals(HashedPassword);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void SetPassword(string password,bool resetSalt=true)
|
||||||
|
{
|
||||||
|
if(resetSalt) Salt=GetNewSalt();
|
||||||
|
|
||||||
|
string pass = $"{password}{Salt}";
|
||||||
|
|
||||||
|
using(SHA256 mySHA256 = SHA256.Create())
|
||||||
|
{
|
||||||
|
string check= Convert.ToBase64String(mySHA256.ComputeHash(System.Text.Encoding.UTF8.GetBytes(pass)));
|
||||||
|
HashedPassword=check;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
public static string GetNewSalt()
|
||||||
|
{
|
||||||
|
return Session.GetNewId();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
using Tesses.WebServer;
|
||||||
|
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class AppConfig
|
||||||
|
{
|
||||||
|
internal AppConfig(ServerContext ctx,Arguments args)
|
||||||
|
{
|
||||||
|
|
||||||
|
bool hasWSPort=false;
|
||||||
|
|
||||||
|
string ws_port_str;
|
||||||
|
ushort ws_port;
|
||||||
|
if(ctx.RequestHeaders.TryGetFirst("WS-Port",out ws_port_str))
|
||||||
|
{
|
||||||
|
if(ushort.TryParse(ws_port_str,out ws_port))
|
||||||
|
{
|
||||||
|
hasWSPort=true;
|
||||||
|
WSPort=ws_port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!hasWSPort)
|
||||||
|
{
|
||||||
|
if(args.TryGetValueNotNull("ws-port",out ws_port_str))
|
||||||
|
{
|
||||||
|
if(ushort.TryParse(ws_port_str,out ws_port))
|
||||||
|
{
|
||||||
|
hasWSPort=true;
|
||||||
|
WSPort=ws_port;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
WSPort=4030;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hasWSPort=false; //reusing bool to save ram
|
||||||
|
if(ctx.RequestHeaders.TryGetFirst("WS-Secure",out ws_port_str)) //reusing string to save ram
|
||||||
|
{
|
||||||
|
if(ws_port_str=="true") {
|
||||||
|
hasWSPort=true;
|
||||||
|
WSS=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!hasWSPort)
|
||||||
|
{
|
||||||
|
if(args.TryGetValue("ws-secure",out ws_port_str))
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(ws_port_str))
|
||||||
|
{
|
||||||
|
WSS=true;
|
||||||
|
}else{
|
||||||
|
WSS = ws_port_str=="true";
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
WSS=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(ctx.QueryParams.TryGetFirst("Chatr-Session",out ws_port_str))
|
||||||
|
{
|
||||||
|
|
||||||
|
SessionId=ws_port_str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string SessionId {get;set;}
|
||||||
|
public ushort WSPort {get;set;}
|
||||||
|
public bool WSS {get;set;}
|
||||||
|
public bool WS_SAME_AS_HTTP {get{return WebSocketSameAsHttp;}set {WebSocketSameAsHttp=value;}}
|
||||||
|
|
||||||
|
public static bool WebSocketSameAsHttp {get;set;}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System;
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class Arguments
|
||||||
|
{
|
||||||
|
public Arguments(string[] args)
|
||||||
|
{
|
||||||
|
//--key={value}
|
||||||
|
|
||||||
|
foreach(var arg in args)
|
||||||
|
{
|
||||||
|
string[] argument=arg.Split(new char[]{'='},2,StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
if(argument.Length >= 1)
|
||||||
|
{
|
||||||
|
string key=argument[0];
|
||||||
|
string value=null;
|
||||||
|
if(argument.Length ==2) value=argument[1];
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Dictionary<string,string> _args=new Dictionary<string, string>();
|
||||||
|
public IReadOnlyDictionary<string,string> ArgumentList {get{return _args;}}
|
||||||
|
|
||||||
|
public static implicit operator Dictionary<string,string>(Arguments args)
|
||||||
|
{
|
||||||
|
Dictionary<string,string> dict=new Dictionary<string, string>();
|
||||||
|
foreach(var di in args.ArgumentList)
|
||||||
|
{
|
||||||
|
dict.Add(di.Key,di.Value);
|
||||||
|
}
|
||||||
|
return dict;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContainsKey(string key)
|
||||||
|
{
|
||||||
|
return ArgumentList.ContainsKey(key);
|
||||||
|
}
|
||||||
|
public bool TryGetValue(string key,out string value)
|
||||||
|
{
|
||||||
|
|
||||||
|
return ArgumentList.TryGetValue(key,out value);
|
||||||
|
}
|
||||||
|
public bool TryGetValueNotNull(string key,out string value)
|
||||||
|
{
|
||||||
|
string value0;
|
||||||
|
if(TryGetValue(key,out value0))
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(value0)) {value=value0; return true;}
|
||||||
|
}
|
||||||
|
value="";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class AuthenticationResult
|
||||||
|
{
|
||||||
|
public AuthenticationResult(string error)
|
||||||
|
{
|
||||||
|
Success=UserManagement.AllowLoginAsAnnonymous;
|
||||||
|
Account = UserManagement.GetAnonymous();
|
||||||
|
|
||||||
|
Session=new Session() {RememberMe=true,SessionId=""};
|
||||||
|
NotAnonymous=false;
|
||||||
|
}
|
||||||
|
public AuthenticationResult(Account account,Session session)
|
||||||
|
{
|
||||||
|
Success=true;
|
||||||
|
Account =account;
|
||||||
|
Session=session;
|
||||||
|
NotAnonymous=true;
|
||||||
|
}
|
||||||
|
public bool NotAnonymous {get;set;}
|
||||||
|
public bool Success {get;set;}
|
||||||
|
public string Error {get;set;}
|
||||||
|
|
||||||
|
public Account Account {get;set;}
|
||||||
|
public Session Session {get;set;}
|
||||||
|
}
|
||||||
|
public class AuthenticationBotResult
|
||||||
|
{
|
||||||
|
public AuthenticationBotResult(Bot bot)
|
||||||
|
{
|
||||||
|
Bot=bot;
|
||||||
|
Success=true;
|
||||||
|
}
|
||||||
|
public AuthenticationBotResult(string error)
|
||||||
|
{
|
||||||
|
Success=false;
|
||||||
|
Error=error;
|
||||||
|
}
|
||||||
|
public bool Success {get;set;}
|
||||||
|
public string Error {get;set;}
|
||||||
|
|
||||||
|
public Bot Bot {get;set;}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class Bot
|
||||||
|
{
|
||||||
|
public Bot()
|
||||||
|
{
|
||||||
|
BotUserName="";
|
||||||
|
ApiKey="";
|
||||||
|
BotName="";
|
||||||
|
BotDescription="";
|
||||||
|
Permissions=new Permissions();
|
||||||
|
}
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public long UserId {get;set;}
|
||||||
|
|
||||||
|
public string BotUserName {get;set;}
|
||||||
|
public string BotName {get;set;}
|
||||||
|
public string ApiKey {get;set;}
|
||||||
|
|
||||||
|
|
||||||
|
public string BotDescription {get;set;}
|
||||||
|
|
||||||
|
public Permissions Permissions {get;set;}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="LiteDb" Version="5.0.12" />
|
||||||
|
<PackageReference Include="Scriban" Version="5.5.0" />
|
||||||
|
<PackageReference Include="SIPSorcery.WebSocketSharp" Version="0.0.1" />
|
||||||
|
<PackageReference Include="Tesses.WebServer" Version="1.0.3.7" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\ChatrCommon\ChatrCommon.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,16 @@
|
||||||
|
using System;
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class LoginResult
|
||||||
|
{
|
||||||
|
public const string SessionCookieName="Chatr-Session";
|
||||||
|
|
||||||
|
public string SessionId {get;set;}
|
||||||
|
|
||||||
|
public DateTime Expires {get;set;}
|
||||||
|
|
||||||
|
public Account Account {get;set;}
|
||||||
|
|
||||||
|
public bool Success {get;set;}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Chatr;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class Message
|
||||||
|
{
|
||||||
|
public Message()
|
||||||
|
{
|
||||||
|
CreationTime=new DateTime(1970,1,1);
|
||||||
|
Content="";
|
||||||
|
IsFromBot=false;
|
||||||
|
IsToBot=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DateTime CreationTime {get;set;}
|
||||||
|
public long Id {get;set;}
|
||||||
|
public long SourceUserId {get;set;}
|
||||||
|
|
||||||
|
public bool IsFromBot {get;set;}
|
||||||
|
|
||||||
|
public long SourceBotId {get;set;}
|
||||||
|
|
||||||
|
public bool IsToBot {get;set;}
|
||||||
|
|
||||||
|
public long DestinationBotId {get;set;}
|
||||||
|
public long DestinationUserId {get;set;}
|
||||||
|
|
||||||
|
public string Content {get;set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Notification
|
||||||
|
{
|
||||||
|
public Notification()
|
||||||
|
{
|
||||||
|
Title="";
|
||||||
|
Body="";
|
||||||
|
OnClick=new NotificationEvent();
|
||||||
|
NotificationButtons=new List<NotificationButton>();
|
||||||
|
|
||||||
|
BotNotificationId="";
|
||||||
|
}
|
||||||
|
public string BotNotificationId {get;set;}
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
|
||||||
|
public string Title {get;set;}
|
||||||
|
|
||||||
|
public string Body {get;set;}
|
||||||
|
|
||||||
|
public long DestinationUserId {get;set;}
|
||||||
|
|
||||||
|
public long SourceUserId {get;set;}
|
||||||
|
|
||||||
|
public long SourceBotId {get;set;}
|
||||||
|
|
||||||
|
public string BotUserString {get;set;}
|
||||||
|
//no dest bot id
|
||||||
|
public NotificationEvent OnClick {get;set;}
|
||||||
|
|
||||||
|
public List<NotificationButton> NotificationButtons {get;set;}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
using Tesses.WebServer;
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class Permissions
|
||||||
|
{
|
||||||
|
///<summary>
|
||||||
|
/// Can the bot send messages to other users
|
||||||
|
///</summary>
|
||||||
|
public bool CanSendMessagesToOtherPeople {get;set;}
|
||||||
|
///<summary>
|
||||||
|
/// Can other bots receive messages from this bot
|
||||||
|
///</summary>
|
||||||
|
public bool OthersCanReceiveMessages {get;set;}
|
||||||
|
|
||||||
|
///<summary>
|
||||||
|
/// Can this bot send notifications
|
||||||
|
///</summary>
|
||||||
|
public bool CanSendNotifications {get;set;}
|
||||||
|
///<summary>
|
||||||
|
///Bot Can pull data from share target
|
||||||
|
///</summary>
|
||||||
|
public bool CanGetFromShareTarget {get;set;}
|
||||||
|
///<summary>
|
||||||
|
///Bot Can Receive Messages
|
||||||
|
///</summary>
|
||||||
|
|
||||||
|
public bool CanGetMessages { get; set; }
|
||||||
|
|
||||||
|
public void SetPermissionsFromForm(ServerContext ctx)
|
||||||
|
{
|
||||||
|
CanSendMessagesToOtherPeople=GetPermissionValue(ctx,"other_people");
|
||||||
|
OthersCanReceiveMessages=GetPermissionValue(ctx,"bot2bot");
|
||||||
|
CanSendNotifications=GetPermissionValue(ctx,"notification");
|
||||||
|
CanGetFromShareTarget=GetPermissionValue(ctx,"share_target");
|
||||||
|
CanGetMessages=GetPermissionValue(ctx,"recv_msg");
|
||||||
|
|
||||||
|
}
|
||||||
|
private bool GetPermissionValue(ServerContext ctx,string key)
|
||||||
|
{
|
||||||
|
string res;
|
||||||
|
if(ctx.QueryParams.TryGetFirst(key,out res))
|
||||||
|
{
|
||||||
|
return res=="on";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,977 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Scriban;
|
||||||
|
using Tesses.WebServer;
|
||||||
|
using WebSocketSharp.Server;
|
||||||
|
using Chatr;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Tesses.Chatr.Server;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
public class ChatrApp
|
||||||
|
{
|
||||||
|
public static async Task Init(Arguments arguments,Func<string,string> getPath,IPEndPoint http,WebSocketServer wssv,Action wsInit=null)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
using(var token = new CancellationTokenSource()){
|
||||||
|
Console.CancelKeyPress +=(sender,e)=>{token.Cancel();};
|
||||||
|
|
||||||
|
|
||||||
|
var template_index = Template.Parse(File.ReadAllText(getPath("index.html")));
|
||||||
|
var template_user = Template.Parse(File.ReadAllText(getPath("user/index.html")));
|
||||||
|
var template_qr = Template.Parse(File.ReadAllText(getPath("device-qr/index.html")));
|
||||||
|
RouteServer svr=new RouteServer();
|
||||||
|
/*svr.Add("/dem",async(e)=>{
|
||||||
|
var data= QRCoder.PngByteQRCodeHelper.GetQRCode("Demi Lovato",QRCoder.QRCodeGenerator.ECCLevel.Q,640);
|
||||||
|
await e.SendBytesAsync(data,"image/png");
|
||||||
|
});*/
|
||||||
|
svr.Add("/onetimeauth",async(e)=>{
|
||||||
|
string key;
|
||||||
|
if(e.QueryParams.TryGetFirst("key",out key))
|
||||||
|
{
|
||||||
|
|
||||||
|
await e.SendTextAsync($"<!doctype html><html><head><title>Login</title></head><body><form method=\"POST\" action=\"./onetimeauth\"><input type=\"submit\" value=\"Login\"><input type=\"hidden\" name=\"key\" value=\"{key}\"></form></body></html>");
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
svr.Add("/share",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
StringBuilder b=new StringBuilder();
|
||||||
|
foreach(var queryParm in e.QueryParams)
|
||||||
|
{
|
||||||
|
foreach(var value in queryParm.Value)
|
||||||
|
{
|
||||||
|
b.AppendLine($"{queryParm.Key}: {value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendTextAsync(b.ToString(),"text/plain");
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/onetimeauth",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
string key;
|
||||||
|
if(e.QueryParams.TryGetFirst("key",out key))
|
||||||
|
{
|
||||||
|
string session;
|
||||||
|
if(UserManagement.GetAuthCode(key,out session))
|
||||||
|
{
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={session}; Path=/");
|
||||||
|
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/appconfig.json",async(e)=>{
|
||||||
|
AppConfig config = new AppConfig(e,arguments);
|
||||||
|
|
||||||
|
await e.SendJsonAsync(config);
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/api/email",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync(res.Account.Email,"text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception ex)
|
||||||
|
{
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("[NO ACCOUNT]","text/plain");
|
||||||
|
});
|
||||||
|
svr.Add("/api/name",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync(res.Account.Name,"text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception ex)
|
||||||
|
{
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("[NO ACCOUNT]","text/plain");
|
||||||
|
});
|
||||||
|
svr.Add("/api/login2",async(e)=>{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
e.ParseBody();
|
||||||
|
string username;
|
||||||
|
string password;
|
||||||
|
|
||||||
|
|
||||||
|
if(e.QueryParams.TryGetFirst("username",out username))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password",out password))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Login(username,password,true);
|
||||||
|
if(res.Success)
|
||||||
|
{
|
||||||
|
//Set-Cookie
|
||||||
|
//if(rememberMe)
|
||||||
|
//{
|
||||||
|
await e.SendTextAsync(res.SessionId,"text/plain");
|
||||||
|
Console.WriteLine("HI");
|
||||||
|
|
||||||
|
//}else{
|
||||||
|
// e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={res.SessionId}; Path=/");
|
||||||
|
//}
|
||||||
|
//await e.SendRedirectAsync("/");
|
||||||
|
}else{
|
||||||
|
e.StatusCode=401;
|
||||||
|
await e.SendTextAsync("Unauthorized");
|
||||||
|
//await e.SendRedirectAsync("/err/incorrect-password.html");
|
||||||
|
|
||||||
|
//do what you do when You Have Wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/botmsg",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
//enforce bot permissions
|
||||||
|
e.StatusCode=400;
|
||||||
|
string msg_to;
|
||||||
|
if(!e.QueryParams.TryGetFirst("message_to",out msg_to)) msg_to="me";
|
||||||
|
|
||||||
|
string apiKey;
|
||||||
|
if(!e.QueryParams.TryGetFirst("ApiKey",out apiKey)) return;
|
||||||
|
|
||||||
|
string body;
|
||||||
|
if(!e.QueryParams.TryGetFirst("body",out body)) body="[No Body]";
|
||||||
|
|
||||||
|
string destbot;
|
||||||
|
if(!e.QueryParams.TryGetFirst("destbot",out destbot)) destbot="";
|
||||||
|
|
||||||
|
bool destIsBot=!string.IsNullOrWhiteSpace(destbot);
|
||||||
|
bool isToMe = msg_to == "me";
|
||||||
|
|
||||||
|
var key=UserManagement.AuthenticateApiKey(apiKey);
|
||||||
|
Func<Bot,string,bool> getIsNotMe=(b,msg_to_who)=>{
|
||||||
|
if(msg_to_who == "me") return false;
|
||||||
|
|
||||||
|
if(DataBase.Accounts ==null) return true;
|
||||||
|
|
||||||
|
var res=DataBase.Accounts.FindById(UserManagement.GetUserId(msg_to_who));
|
||||||
|
if(res == null) return true;
|
||||||
|
return res.Id != b.UserId;
|
||||||
|
};
|
||||||
|
if(key.Success && key.Bot != null)
|
||||||
|
{
|
||||||
|
if(!isToMe && getIsNotMe(key.Bot,msg_to) && key.Bot.Permissions.CanSendMessagesToOtherPeople) { await e.SendTextAsync("Access Denied: Can't Send To Another Person"); return;}
|
||||||
|
if(destIsBot && !key.Bot.Permissions.OthersCanReceiveMessages ) {await e.SendTextAsync("Access Denied: Can't send message to another bot"); return;}
|
||||||
|
|
||||||
|
Message msg=new Message();
|
||||||
|
msg.IsFromBot=true;
|
||||||
|
msg.IsToBot = destIsBot;
|
||||||
|
msg.SourceBotId = key.Bot.Id;
|
||||||
|
msg.SourceUserId = key.Bot.UserId;
|
||||||
|
msg.DestinationUserId = isToMe ? key.Bot.UserId : UserManagement.GetUserId(msg_to);
|
||||||
|
if(msg.DestinationUserId == -1)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync("Unknown destination user");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(msg.IsToBot)
|
||||||
|
{
|
||||||
|
var res=UserManagement.GetBotId(msg.DestinationUserId,destbot);
|
||||||
|
if(res == -1)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync("Unknown destination bot");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg.DestinationBotId=res;
|
||||||
|
}
|
||||||
|
msg.Content = body;
|
||||||
|
UserManagement.SendMessage(msg);
|
||||||
|
|
||||||
|
e.StatusCode=200;
|
||||||
|
await e.SendTextAsync("Success");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("No Source Bot");
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/msg",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
string msg_to;
|
||||||
|
bool msg_to_me=true;
|
||||||
|
|
||||||
|
string redirect_to;
|
||||||
|
string body;
|
||||||
|
if(!e.QueryParams.TryGetFirst("body",out body)) body="[No Body]";
|
||||||
|
if(!e.QueryParams.TryGetFirst("redirect_to",out redirect_to)) redirect_to="/";
|
||||||
|
long dest_id=-1;
|
||||||
|
if(e.QueryParams.TryGetFirst("message_to",out msg_to)){
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(msg_to))
|
||||||
|
{
|
||||||
|
dest_id=UserManagement.GetUserId(msg_to);
|
||||||
|
if(dest_id>-1)
|
||||||
|
{
|
||||||
|
msg_to_me=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//long dest_id =
|
||||||
|
/*<input type="hidden" name="message_to" value="{{ uname }}">
|
||||||
|
<input type="hidden" name="redirect_to" value="/user?name={{ uname }}">
|
||||||
|
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<textarea class="mdl-textfield__input" type="text" rows= "6" id="sample5" name="body" ></textarea>
|
||||||
|
<label class="mdl-textfield__label" for="sample5">Message Body</label>
|
||||||
|
|
||||||
|
</div>*/
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
//from user
|
||||||
|
Message msg=new Message();
|
||||||
|
msg.IsFromBot=false;
|
||||||
|
msg.IsToBot=false;
|
||||||
|
msg.SourceUserId = res.Account.Id;
|
||||||
|
|
||||||
|
if(msg_to_me) msg.DestinationUserId=msg.SourceUserId; else msg.DestinationUserId=dest_id;
|
||||||
|
msg.Content=body;
|
||||||
|
UserManagement.SendMessage(msg);
|
||||||
|
|
||||||
|
await e.SendRedirectAsync(redirect_to);return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!msg_to_me)
|
||||||
|
{
|
||||||
|
var account =UserManagement.GetAnonymous();
|
||||||
|
if(account != null && account.IsCorrectPassword("4209enabled")){
|
||||||
|
Message msg=new Message();
|
||||||
|
msg.IsFromBot=false;
|
||||||
|
msg.IsToBot=false;
|
||||||
|
msg.SourceUserId = account.Id; //
|
||||||
|
if(msg_to_me) msg.DestinationUserId=msg.SourceUserId; else msg.DestinationUserId=dest_id;
|
||||||
|
msg.Content=body;
|
||||||
|
UserManagement.SendMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync(redirect_to);return;
|
||||||
|
},"POST"); //send msg from page
|
||||||
|
svr.Add("/api/logout",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null && res.Session != null)
|
||||||
|
{
|
||||||
|
string session_id=cookie[1];
|
||||||
|
UserManagement.Logout(session_id);
|
||||||
|
DateTime past=new DateTime(2018,7,24,14,55,00); //some bloke Overdoses on this date
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={session_id}; Path=/; Expires={past.ToString("R")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
});
|
||||||
|
svr.Add("/apikey",async(e)=>{
|
||||||
|
//await e.SendRedirectAsync($"/apikey?name={bot.BotUserName}");
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
string username;
|
||||||
|
if(e.QueryParams.TryGetFirst("name",out username)){
|
||||||
|
Bot bot;
|
||||||
|
if(DataBase.Bots !=null && (bot=DataBase.Bots.FindOne(ee=>ee.UserId==res.Account.Id && ee.BotUserName==username))!=null)
|
||||||
|
{
|
||||||
|
string key= File.ReadAllText(getPath("apikey/index.html")).Replace("{API_KEY}",bot.ApiKey);
|
||||||
|
await e.SendTextAsync(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("Unauthorized user or no bot with specified username");
|
||||||
|
});
|
||||||
|
svr.Add("/user",async(e)=>{
|
||||||
|
|
||||||
|
if(DataBase.Accounts ==null) {
|
||||||
|
await e.SendTextAsync("DataBase error: Accounts is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Account account=null;
|
||||||
|
string username;
|
||||||
|
if(e.QueryParams.TryGetFirst("name",out username))
|
||||||
|
{
|
||||||
|
|
||||||
|
account=DataBase.Accounts.FindOne(ee=>ee.Username==username);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName=res.Account.Username;
|
||||||
|
long accountUserId =res.Account.Id;
|
||||||
|
if(account == null)
|
||||||
|
{
|
||||||
|
account = res.Account;
|
||||||
|
}
|
||||||
|
object logged_in=new{
|
||||||
|
Name=WebUtility.HtmlEncode(accountName),
|
||||||
|
Url=$"./user?name={WebUtility.UrlEncode(accountUserName)}",
|
||||||
|
Notloggedin=false,
|
||||||
|
Loggedin=true,
|
||||||
|
Uname=account.Username,
|
||||||
|
Loggedinme=account.Id == res.Account.Id,
|
||||||
|
Loggedinnotme=account.Id != res.Account.Id,
|
||||||
|
Othername=WebUtility.HtmlEncode(account.Name),
|
||||||
|
Opt=UserManagement.GetUserOptions(account)
|
||||||
|
};
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_user.Render(logged_in));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(account == null) {
|
||||||
|
|
||||||
|
await e.SendTextAsync("No Account");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_user.Render(new{
|
||||||
|
Url="./login",
|
||||||
|
Name="Login",
|
||||||
|
Loggedinnotme=false,
|
||||||
|
Loggedinme=false,
|
||||||
|
Loggedin=false,
|
||||||
|
Notloggedin=true,
|
||||||
|
Uname=account.Username,
|
||||||
|
Othername=WebUtility.HtmlEncode(account.Name)
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
//await e.SendFileAsync(getPath("user/index.html"));
|
||||||
|
});
|
||||||
|
svr.Add("/api/create-bot",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
e.ParseBody();
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
string botname;
|
||||||
|
string botuser;
|
||||||
|
string botdesc;
|
||||||
|
|
||||||
|
if(e.QueryParams.TryGetFirst("botname",out botname))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botuser",out botuser))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botdesc",out botdesc))
|
||||||
|
{
|
||||||
|
if(DataBase.Bots != null && Account.IsValidName(botname) && Account.IsValidUserName(botuser) && Account.IsValidName(botdesc)){
|
||||||
|
var bot= DataBase.Bots.FindOne(ee=>ee.BotUserName == botuser && res.Account.Id == ee.UserId);
|
||||||
|
if(bot == null)
|
||||||
|
{
|
||||||
|
Bot b=new Bot();
|
||||||
|
b.UserId= res.Account.Id;
|
||||||
|
b.BotUserName=botuser;
|
||||||
|
b.BotDescription=botdesc;
|
||||||
|
b.BotName = botname;
|
||||||
|
b.ApiKey = Account.GetNewSalt();
|
||||||
|
b.Permissions.SetPermissionsFromForm(e);
|
||||||
|
DataBase.Bots.Insert(b);
|
||||||
|
await e.SendRedirectAsync($"/apikey?name={b.BotUserName}");
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
bot.Permissions.SetPermissionsFromForm(e);
|
||||||
|
DataBase.Bots.Update(bot);
|
||||||
|
await e.SendRedirectAsync($"/apikey?name={bot.BotUserName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},"POST");
|
||||||
|
|
||||||
|
svr.Add("/create-bot-permissions",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
e.ParseBody();
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
//get botname botuser botdesc
|
||||||
|
string botname;
|
||||||
|
string botuser;
|
||||||
|
string botdesc;
|
||||||
|
if(e.QueryParams.TryGetFirst("botname",out botname))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botuser",out botuser))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botdesc",out botdesc))
|
||||||
|
{
|
||||||
|
if(DataBase.Bots != null){
|
||||||
|
var bot= DataBase.Bots.FindOne(ee=>ee.BotUserName == botuser && res.Account.Id == ee.UserId);
|
||||||
|
|
||||||
|
if(bot == null && Account.IsValidName(botname) && Account.IsValidUserName(botuser) && Account.IsValidName(botdesc))
|
||||||
|
{
|
||||||
|
string page0=File.ReadAllText(getPath("create-bot-permissions/index.html")).Replace("{BOT_DESC}",botdesc).Replace("{BOT_USER}",botuser).Replace("{BOT_NAME}",botname);
|
||||||
|
await e.SendTextAsync(page0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("Either your not logged in or post error");
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/create-bot",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath("create-bot/index.html"));
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/api/user-setting",async(e)=>{
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
await e.SendJsonAsync(UserManagement.GetUserOptions(res.Account));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/api/user-setting",async(e)=>{
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
var opt=UserManagement.GetUserOptions(res.Account);
|
||||||
|
UserManagement.SetUserOptions(opt,e);
|
||||||
|
await e.SendRedirectAsync($"/user?name={res.Account.Username}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},"POST");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
svr.Add("/",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var mymsg=new List<MyMessage>();
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,null))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Mine=true;
|
||||||
|
mymsg.Add(msg0);
|
||||||
|
}
|
||||||
|
var users = new List<UserAccount>();
|
||||||
|
|
||||||
|
foreach(var bot in UserManagement.GetMyBots(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = $"me-{bot.BotUserName}";
|
||||||
|
usr.Text = WebUtility.HtmlEncode($"{bot.BotName} (My Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0 =new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Mine = msg.IsToBot;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
}
|
||||||
|
foreach(var otherUser in UserManagement.GetUserWithMe(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = otherUser.Username;
|
||||||
|
|
||||||
|
usr.Text = WebUtility.HtmlEncode(otherUser.Name);
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
foreach(var bot in UserManagement.GetBotsWithMe(res.Account,otherUser))
|
||||||
|
{
|
||||||
|
var usr0 = new UserAccount();
|
||||||
|
usr0.Hash = $"{otherUser.Username}-{bot.BotUserName}";
|
||||||
|
usr0.Text = WebUtility.HtmlDecode($"{bot.BotName} ({otherUser.Name}'s Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
usr0.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName =res.Account.Username;
|
||||||
|
object logged_in=new{
|
||||||
|
Name=WebUtility.HtmlEncode(accountName),
|
||||||
|
Url=$"./user?name={accountUserName}",
|
||||||
|
Loggedin=true,
|
||||||
|
Mymessages=mymsg,
|
||||||
|
Users=users
|
||||||
|
};
|
||||||
|
await e.SendTextAsync(template_index.Render(logged_in));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}catch (Exception ex){
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_index.Render(new{
|
||||||
|
Url="./login",
|
||||||
|
Name="Login",
|
||||||
|
Loggedin=false
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
svr.Add("/api/new-ota.txt",async(e)=>{
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
DateTime expire=DateTime.Now.AddYears(1);
|
||||||
|
if(e.QueryParams.TryGetFirst("expires",out cookie_txt))
|
||||||
|
{
|
||||||
|
long exp;
|
||||||
|
if(long.TryParse(cookie_txt,out exp))
|
||||||
|
{
|
||||||
|
expire=DateTimeOffset.FromUnixTimeSeconds(exp).DateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string session=UserManagement.CreateAnotherSession(cookie[1],expire);
|
||||||
|
if(!string.IsNullOrWhiteSpace(session))
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
await e.SendTextAsync(UserManagement.CreateOTA(session),"text/plain");return;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.StatusCode=401;
|
||||||
|
await e.WriteHeadersAsync();
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/device-qr",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName =res.Account.Username;
|
||||||
|
//string? session=UserManagement.CreateAnotherSession(cookie[1],DateTime.Now.AddYears(1));
|
||||||
|
/*
|
||||||
|
Console.WriteLine($"{e.Host}#{session}");
|
||||||
|
*/
|
||||||
|
|
||||||
|
object logged_in=new{
|
||||||
|
Name=WebUtility.HtmlEncode(accountName),
|
||||||
|
Url=$"./user?name={WebUtility.UrlEncode(accountUserName)}",
|
||||||
|
Loggedin=true,
|
||||||
|
};
|
||||||
|
await e.SendTextAsync(template_qr.Render(logged_in));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_qr.Render(new{
|
||||||
|
Url="./login",
|
||||||
|
Name="Login",
|
||||||
|
Loggedin=false
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
svr.Add("/api/state.json",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var mymsg=new List<MyMessage>();
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,null))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Mine=true;
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
mymsg.Add(msg0);
|
||||||
|
}
|
||||||
|
var users = new List<UserAccount>();
|
||||||
|
|
||||||
|
foreach(var bot in UserManagement.GetMyBots(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = $"me-{bot.BotUserName}";
|
||||||
|
usr.Text = WebUtility.HtmlEncode($"{bot.BotName} (My Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0 =new MyMessage();
|
||||||
|
msg0.Mine = msg.IsToBot;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
}
|
||||||
|
foreach(var otherUser in UserManagement.GetUserWithMe(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = otherUser.Username;
|
||||||
|
|
||||||
|
usr.Text = WebUtility.HtmlEncode(otherUser.Name);
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
foreach(var bot in UserManagement.GetBotsWithMe(res.Account,otherUser))
|
||||||
|
{
|
||||||
|
var usr0 = new UserAccount();
|
||||||
|
usr0.Hash = $"{otherUser.Username}-{bot.BotUserName}";
|
||||||
|
usr0.Text = WebUtility.HtmlDecode($"{bot.BotName} ({otherUser.Name}'s Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
usr0.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName =res.Account.Username;
|
||||||
|
object logged_in=new{
|
||||||
|
Email=res.Account.Email,
|
||||||
|
Name=accountName,
|
||||||
|
Url=$"/user?name={accountUserName}",
|
||||||
|
Loggedin=true,
|
||||||
|
Mymessages=mymsg,
|
||||||
|
Users=users
|
||||||
|
};
|
||||||
|
Console.WriteLine("Alternate HI");
|
||||||
|
await e.SendJsonAsync(logged_in);
|
||||||
|
Console.WriteLine("SENT");
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}catch (Exception ex){
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
await e.SendJsonAsync(new{
|
||||||
|
Loggedin=false
|
||||||
|
});
|
||||||
|
|
||||||
|
},"GET");
|
||||||
|
svr.Add($"/login",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath("login/index.html"));
|
||||||
|
});
|
||||||
|
svr.Add($"/signup",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath("signup/index.html"));
|
||||||
|
});
|
||||||
|
Action<string> set_url=(e2)=>{
|
||||||
|
svr.Add($"/{e2}",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath(e2));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
foreach(var f in File.ReadAllLines(getPath("files.txt")))
|
||||||
|
{
|
||||||
|
set_url(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
svr.Add("/api/login",async(e)=>{
|
||||||
|
|
||||||
|
|
||||||
|
e.ParseBody();
|
||||||
|
string username;
|
||||||
|
string password;
|
||||||
|
string rememberMeStr;
|
||||||
|
bool rememberMe;
|
||||||
|
|
||||||
|
if(e.QueryParams.TryGetFirst("username",out username))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password",out password))
|
||||||
|
{
|
||||||
|
rememberMe=(e.QueryParams.TryGetFirst("remember",out rememberMeStr) && rememberMeStr == "on");
|
||||||
|
var res=UserManagement.Login(username,password,rememberMe);
|
||||||
|
if(res.Success)
|
||||||
|
{
|
||||||
|
//Set-Cookie
|
||||||
|
if(rememberMe)
|
||||||
|
{
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={res.SessionId}; Path=/; Expires={res.Expires.ToString("R")}");
|
||||||
|
|
||||||
|
}else{
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={res.SessionId}; Path=/");
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
}else{
|
||||||
|
await e.SendRedirectAsync("/err/incorrect-password.html");
|
||||||
|
|
||||||
|
//do what you do when You Have Wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/signup",async(e)=>{
|
||||||
|
|
||||||
|
e.ParseBody();
|
||||||
|
string email;
|
||||||
|
string name;
|
||||||
|
string username;
|
||||||
|
string password;
|
||||||
|
string confirm;
|
||||||
|
if(e.QueryParams.TryGetFirst("email",out email))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("name",out name))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("uname",out username))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password",out password))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password2",out confirm))
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password) && !string.IsNullOrWhiteSpace(confirm))
|
||||||
|
{
|
||||||
|
RegistrationResult result=UserManagement.RegisterAccount(name,username,email,password);
|
||||||
|
|
||||||
|
if(result.Success)
|
||||||
|
{
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
object o=new{
|
||||||
|
success=false,
|
||||||
|
error=result.Error
|
||||||
|
};
|
||||||
|
e.StatusCode=400;
|
||||||
|
await e.SendJsonAsync(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.StatusCode=400;
|
||||||
|
object op=new{
|
||||||
|
success=false,
|
||||||
|
error="Information not filled out"
|
||||||
|
};
|
||||||
|
await e.SendJsonAsync(op);
|
||||||
|
},"POST");
|
||||||
|
|
||||||
|
MountableServer _mountable=new MountableServer(svr);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HttpServerListener listener=new HttpServerListener(http,_mountable);
|
||||||
|
|
||||||
|
Timer t=new Timer((e)=>{
|
||||||
|
try{
|
||||||
|
DateTime d=DateTime.Now;
|
||||||
|
List<long> items=new List<long>();
|
||||||
|
if(DataBase.Sessions != null){
|
||||||
|
|
||||||
|
foreach(var s in DataBase.Sessions.FindAll())
|
||||||
|
{
|
||||||
|
if(s.Expires < d)
|
||||||
|
{
|
||||||
|
items.Add(s.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(var item in items){
|
||||||
|
DataBase.Sessions.Delete(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception ex)
|
||||||
|
{
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
},null,0,600000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wssv.AddWebSocketService<WSConnection>("/chatr-ws");
|
||||||
|
wssv.Start();
|
||||||
|
if(wsInit != null)
|
||||||
|
{
|
||||||
|
wsInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
await listener.ListenAsync(token.Token);
|
||||||
|
wssv.Stop();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class RegistrationResult
|
||||||
|
{
|
||||||
|
public RegistrationResult(Account account)
|
||||||
|
{
|
||||||
|
Account = account;
|
||||||
|
Success=true;
|
||||||
|
}
|
||||||
|
public RegistrationResult(string error)
|
||||||
|
{
|
||||||
|
Error=error;
|
||||||
|
Success=false;
|
||||||
|
}
|
||||||
|
public Account Account {get;set;}
|
||||||
|
public string Error {get;set;}
|
||||||
|
|
||||||
|
public bool Success {get;set;}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System;
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class Session
|
||||||
|
{
|
||||||
|
|
||||||
|
public Session()
|
||||||
|
{
|
||||||
|
SessionId="";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static string GetNewId()
|
||||||
|
{
|
||||||
|
byte[] data= new byte[32];
|
||||||
|
RandomNumberGenerator.Create().GetBytes(data);
|
||||||
|
return Convert.ToBase64String(data) ;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public string PrettyName {get;set;}
|
||||||
|
|
||||||
|
public DateTime Created {get;set;}
|
||||||
|
public long Id {get;set;}
|
||||||
|
|
||||||
|
public string SessionId {get;set;}
|
||||||
|
|
||||||
|
public long UserId {get;set;}
|
||||||
|
|
||||||
|
public bool RememberMe {get;set;}
|
||||||
|
public DateTime Expires {get;set;}
|
||||||
|
public string GetPrettyName()
|
||||||
|
{
|
||||||
|
if(string.IsNullOrWhiteSpace(PrettyName))
|
||||||
|
{
|
||||||
|
return $"Device {Created.ToShortTimeString()} on {Created.ToShortDateString()}";
|
||||||
|
}
|
||||||
|
return PrettyName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,106 @@
|
||||||
|
using System.Net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Tesses.WebServer;
|
||||||
|
using Chatr;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
namespace Tesses.Chatr.Server
|
||||||
|
{
|
||||||
|
public class UserOptions
|
||||||
|
{
|
||||||
|
|
||||||
|
Account a;
|
||||||
|
|
||||||
|
public List<UserAccount> Users {get;set;}
|
||||||
|
|
||||||
|
public List<UserAccount> Bots {get;set;}
|
||||||
|
|
||||||
|
public string Hidebotschecked {get {return a.ShowBots ? "" : "checked"; } set {a.ShowBots=value != "checked";}}
|
||||||
|
|
||||||
|
public string Showuserchecked {get {return a.ShowUserNameToAnyBody ? "checked" : "";} set{a.ShowUserNameToAnyBody=value=="checked";}}
|
||||||
|
|
||||||
|
public UserOptions(Account account)
|
||||||
|
{
|
||||||
|
Users=new List<UserAccount>();
|
||||||
|
Bots=new List<UserAccount>();
|
||||||
|
a=account;
|
||||||
|
|
||||||
|
foreach(var user in UserManagement.GetUserWithMe(account,false))
|
||||||
|
{
|
||||||
|
UserAccount usr=new UserAccount();
|
||||||
|
usr.Hash = user.Username;
|
||||||
|
usr.Text = WebUtility.HtmlEncode(user.Name);
|
||||||
|
usr.UId=user.Id;
|
||||||
|
usr.Shown = account.HiddenUsers.Contains(user.Id) ? "" : "checked";
|
||||||
|
foreach(var bot in UserManagement.GetBotsWithMe(account,user,false))
|
||||||
|
{
|
||||||
|
UserAccount usr1=new UserAccount();
|
||||||
|
usr1.Hash=$"{user.Username}={bot.BotUserName}";
|
||||||
|
usr1.Text = WebUtility.HtmlEncode($"{bot.BotName} ({bot.BotUserName}'s Bot)");
|
||||||
|
usr1.UId = bot.Id;
|
||||||
|
usr1.Shown = account.HiddenBots.Contains(bot.Id) ? "" : "checked";
|
||||||
|
Bots.Add(usr1);
|
||||||
|
}
|
||||||
|
Users.Add(usr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Set(ServerContext ctx,bool save=true)
|
||||||
|
{
|
||||||
|
string res;
|
||||||
|
if(ctx.QueryParams.TryGetFirst("hide_bots",out res))
|
||||||
|
{
|
||||||
|
Hidebotschecked=res=="on" ? "checked" : "";
|
||||||
|
}
|
||||||
|
if(ctx.QueryParams.TryGetFirst("show_user",out res))
|
||||||
|
{
|
||||||
|
Showuserchecked=res=="on" ? "checked" : "";
|
||||||
|
}
|
||||||
|
foreach(var user in Users)
|
||||||
|
{
|
||||||
|
//user
|
||||||
|
user.Shown="";
|
||||||
|
|
||||||
|
if(ctx.QueryParams.TryGetFirst($"user{user.Hash}",out res))
|
||||||
|
{
|
||||||
|
user.Shown = res == "on" ? "checked" : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(var bot in Bots)
|
||||||
|
{
|
||||||
|
bot.Shown="";
|
||||||
|
|
||||||
|
if(ctx.QueryParams.TryGetFirst($"bot{bot.Hash}",out res))
|
||||||
|
{
|
||||||
|
bot.Shown = res == "on" ? "checked" : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(save)
|
||||||
|
{
|
||||||
|
Save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
a.HiddenUsers.Clear();
|
||||||
|
a.HiddenBots.Clear();
|
||||||
|
//only do users and bots
|
||||||
|
//the other bools are already set
|
||||||
|
foreach(var user in Users)
|
||||||
|
{
|
||||||
|
if(user.Shown == "")
|
||||||
|
{
|
||||||
|
a.HiddenUsers.Add(user.UId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(var bot in Users)
|
||||||
|
{
|
||||||
|
if(bot.Shown == "")
|
||||||
|
{
|
||||||
|
a.HiddenBots.Add(bot.UId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(DataBase.Accounts != null)
|
||||||
|
DataBase.Accounts.Update(a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Chatr\Chatr.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="WebSocketProxy" Version="1.0.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
</Project>
|
|
@ -0,0 +1,26 @@
|
||||||
|
using System.Net;
|
||||||
|
using Tesses.Chatr.Server;
|
||||||
|
using WebSocketProxy;
|
||||||
|
|
||||||
|
/*
|
||||||
|
DataBase.SetDb("data.db");
|
||||||
|
UserManagement.DefaultVerificationStatus=true;
|
||||||
|
Func<string,string> getPath=(e)=>{
|
||||||
|
return Path.Combine(page,"wwwroot",e);
|
||||||
|
};*/
|
||||||
|
DataBase.SetDb("data.db");
|
||||||
|
UserManagement.DefaultVerificationStatus=true;
|
||||||
|
string? page=Path.GetDirectoryName(Environment.CurrentDirectory);
|
||||||
|
if(string.IsNullOrWhiteSpace(page))
|
||||||
|
{
|
||||||
|
page=Environment.CurrentDirectory;
|
||||||
|
}
|
||||||
|
Func<string,string> getPath=(e)=>{
|
||||||
|
return Path.Combine(page,"wwwroot",e);
|
||||||
|
};
|
||||||
|
AppConfig.WebSocketSameAsHttp=false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await ChatrApp.Init(new Arguments(args),getPath,new System.Net.IPEndPoint(System.Net.IPAddress.Any,4031),new WebSocketSharp.Server.WebSocketServer(System.Net.IPAddress.Any,4030));
|
||||||
|
|
|
@ -0,0 +1,977 @@
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Net;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Scriban;
|
||||||
|
using Tesses.WebServer;
|
||||||
|
using WebSocketSharp.Server;
|
||||||
|
using Chatr;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Tesses.Chatr.Server;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
public class ChatrApp
|
||||||
|
{
|
||||||
|
public static async Task Init(Arguments arguments,Func<string,string> getPath,IPEndPoint http,WebSocketServer wssv,Action wsInit=null)
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
using(var token = new CancellationTokenSource()){
|
||||||
|
Console.CancelKeyPress +=(sender,e)=>{token.Cancel();};
|
||||||
|
|
||||||
|
|
||||||
|
var template_index = Template.Parse(File.ReadAllText(getPath("index.html")));
|
||||||
|
var template_user = Template.Parse(File.ReadAllText(getPath("user/index.html")));
|
||||||
|
var template_qr = Template.Parse(File.ReadAllText(getPath("device-qr/index.html")));
|
||||||
|
RouteServer svr=new RouteServer();
|
||||||
|
/*svr.Add("/dem",async(e)=>{
|
||||||
|
var data= QRCoder.PngByteQRCodeHelper.GetQRCode("Demi Lovato",QRCoder.QRCodeGenerator.ECCLevel.Q,640);
|
||||||
|
await e.SendBytesAsync(data,"image/png");
|
||||||
|
});*/
|
||||||
|
svr.Add("/onetimeauth",async(e)=>{
|
||||||
|
string key;
|
||||||
|
if(e.QueryParams.TryGetFirst("key",out key))
|
||||||
|
{
|
||||||
|
|
||||||
|
await e.SendTextAsync($"<!doctype html><html><head><title>Login</title></head><body><form method=\"POST\" action=\"./onetimeauth\"><input type=\"submit\" value=\"Login\"><input type=\"hidden\" name=\"key\" value=\"{key}\"></form></body></html>");
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
svr.Add("/share",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
StringBuilder b=new StringBuilder();
|
||||||
|
foreach(var queryParm in e.QueryParams)
|
||||||
|
{
|
||||||
|
foreach(var value in queryParm.Value)
|
||||||
|
{
|
||||||
|
b.AppendLine($"{queryParm.Key}: {value}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendTextAsync(b.ToString(),"text/plain");
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/onetimeauth",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
string key;
|
||||||
|
if(e.QueryParams.TryGetFirst("key",out key))
|
||||||
|
{
|
||||||
|
string session;
|
||||||
|
if(UserManagement.GetAuthCode(key,out session))
|
||||||
|
{
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={session}; Path=/");
|
||||||
|
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
}
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/appconfig.json",async(e)=>{
|
||||||
|
AppConfig config = new AppConfig(e,arguments);
|
||||||
|
|
||||||
|
await e.SendJsonAsync(config);
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/api/email",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync(res.Account.Email,"text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception ex)
|
||||||
|
{
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("[NO ACCOUNT]","text/plain");
|
||||||
|
});
|
||||||
|
svr.Add("/api/name",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync(res.Account.Name,"text/plain");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception ex)
|
||||||
|
{
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("[NO ACCOUNT]","text/plain");
|
||||||
|
});
|
||||||
|
svr.Add("/api/login2",async(e)=>{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
e.ParseBody();
|
||||||
|
string username;
|
||||||
|
string password;
|
||||||
|
|
||||||
|
|
||||||
|
if(e.QueryParams.TryGetFirst("username",out username))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password",out password))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Login(username,password,true);
|
||||||
|
if(res.Success)
|
||||||
|
{
|
||||||
|
//Set-Cookie
|
||||||
|
//if(rememberMe)
|
||||||
|
//{
|
||||||
|
await e.SendTextAsync(res.SessionId,"text/plain");
|
||||||
|
Console.WriteLine("HI");
|
||||||
|
|
||||||
|
//}else{
|
||||||
|
// e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={res.SessionId}; Path=/");
|
||||||
|
//}
|
||||||
|
//await e.SendRedirectAsync("/");
|
||||||
|
}else{
|
||||||
|
e.StatusCode=401;
|
||||||
|
await e.SendTextAsync("Unauthorized");
|
||||||
|
//await e.SendRedirectAsync("/err/incorrect-password.html");
|
||||||
|
|
||||||
|
//do what you do when You Have Wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/botmsg",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
//enforce bot permissions
|
||||||
|
e.StatusCode=400;
|
||||||
|
string msg_to;
|
||||||
|
if(!e.QueryParams.TryGetFirst("message_to",out msg_to)) msg_to="me";
|
||||||
|
|
||||||
|
string apiKey;
|
||||||
|
if(!e.QueryParams.TryGetFirst("ApiKey",out apiKey)) return;
|
||||||
|
|
||||||
|
string body;
|
||||||
|
if(!e.QueryParams.TryGetFirst("body",out body)) body="[No Body]";
|
||||||
|
|
||||||
|
string destbot;
|
||||||
|
if(!e.QueryParams.TryGetFirst("destbot",out destbot)) destbot="";
|
||||||
|
|
||||||
|
bool destIsBot=!string.IsNullOrWhiteSpace(destbot);
|
||||||
|
bool isToMe = msg_to == "me";
|
||||||
|
|
||||||
|
var key=UserManagement.AuthenticateApiKey(apiKey);
|
||||||
|
Func<Bot,string,bool> getIsNotMe=(b,msg_to_who)=>{
|
||||||
|
if(msg_to_who == "me") return false;
|
||||||
|
|
||||||
|
if(DataBase.Accounts ==null) return true;
|
||||||
|
|
||||||
|
var res=DataBase.Accounts.FindById(UserManagement.GetUserId(msg_to_who));
|
||||||
|
if(res == null) return true;
|
||||||
|
return res.Id != b.UserId;
|
||||||
|
};
|
||||||
|
if(key.Success && key.Bot != null)
|
||||||
|
{
|
||||||
|
if(!isToMe && getIsNotMe(key.Bot,msg_to) && key.Bot.Permissions.CanSendMessagesToOtherPeople) { await e.SendTextAsync("Access Denied: Can't Send To Another Person"); return;}
|
||||||
|
if(destIsBot && !key.Bot.Permissions.OthersCanReceiveMessages ) {await e.SendTextAsync("Access Denied: Can't send message to another bot"); return;}
|
||||||
|
|
||||||
|
Message msg=new Message();
|
||||||
|
msg.IsFromBot=true;
|
||||||
|
msg.IsToBot = destIsBot;
|
||||||
|
msg.SourceBotId = key.Bot.Id;
|
||||||
|
msg.SourceUserId = key.Bot.UserId;
|
||||||
|
msg.DestinationUserId = isToMe ? key.Bot.UserId : UserManagement.GetUserId(msg_to);
|
||||||
|
if(msg.DestinationUserId == -1)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync("Unknown destination user");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(msg.IsToBot)
|
||||||
|
{
|
||||||
|
var res=UserManagement.GetBotId(msg.DestinationUserId,destbot);
|
||||||
|
if(res == -1)
|
||||||
|
{
|
||||||
|
await e.SendTextAsync("Unknown destination bot");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
msg.DestinationBotId=res;
|
||||||
|
}
|
||||||
|
msg.Content = body;
|
||||||
|
UserManagement.SendMessage(msg);
|
||||||
|
|
||||||
|
e.StatusCode=200;
|
||||||
|
await e.SendTextAsync("Success");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("No Source Bot");
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/msg",async(e)=>{
|
||||||
|
e.ParseBody();
|
||||||
|
string msg_to;
|
||||||
|
bool msg_to_me=true;
|
||||||
|
|
||||||
|
string redirect_to;
|
||||||
|
string body;
|
||||||
|
if(!e.QueryParams.TryGetFirst("body",out body)) body="[No Body]";
|
||||||
|
if(!e.QueryParams.TryGetFirst("redirect_to",out redirect_to)) redirect_to="/";
|
||||||
|
long dest_id=-1;
|
||||||
|
if(e.QueryParams.TryGetFirst("message_to",out msg_to)){
|
||||||
|
|
||||||
|
if(!string.IsNullOrWhiteSpace(msg_to))
|
||||||
|
{
|
||||||
|
dest_id=UserManagement.GetUserId(msg_to);
|
||||||
|
if(dest_id>-1)
|
||||||
|
{
|
||||||
|
msg_to_me=false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//long dest_id =
|
||||||
|
/*<input type="hidden" name="message_to" value="{{ uname }}">
|
||||||
|
<input type="hidden" name="redirect_to" value="/user?name={{ uname }}">
|
||||||
|
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<textarea class="mdl-textfield__input" type="text" rows= "6" id="sample5" name="body" ></textarea>
|
||||||
|
<label class="mdl-textfield__label" for="sample5">Message Body</label>
|
||||||
|
|
||||||
|
</div>*/
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
//from user
|
||||||
|
Message msg=new Message();
|
||||||
|
msg.IsFromBot=false;
|
||||||
|
msg.IsToBot=false;
|
||||||
|
msg.SourceUserId = res.Account.Id;
|
||||||
|
|
||||||
|
if(msg_to_me) msg.DestinationUserId=msg.SourceUserId; else msg.DestinationUserId=dest_id;
|
||||||
|
msg.Content=body;
|
||||||
|
UserManagement.SendMessage(msg);
|
||||||
|
|
||||||
|
await e.SendRedirectAsync(redirect_to);return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!msg_to_me)
|
||||||
|
{
|
||||||
|
var account =UserManagement.GetAnonymous();
|
||||||
|
if(account != null && account.IsCorrectPassword("4209enabled")){
|
||||||
|
Message msg=new Message();
|
||||||
|
msg.IsFromBot=false;
|
||||||
|
msg.IsToBot=false;
|
||||||
|
msg.SourceUserId = account.Id; //
|
||||||
|
if(msg_to_me) msg.DestinationUserId=msg.SourceUserId; else msg.DestinationUserId=dest_id;
|
||||||
|
msg.Content=body;
|
||||||
|
UserManagement.SendMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync(redirect_to);return;
|
||||||
|
},"POST"); //send msg from page
|
||||||
|
svr.Add("/api/logout",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null && res.Session != null)
|
||||||
|
{
|
||||||
|
string session_id=cookie[1];
|
||||||
|
UserManagement.Logout(session_id);
|
||||||
|
DateTime past=new DateTime(2018,7,24,14,55,00); //some bloke Overdoses on this date
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={session_id}; Path=/; Expires={past.ToString("R")}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
});
|
||||||
|
svr.Add("/apikey",async(e)=>{
|
||||||
|
//await e.SendRedirectAsync($"/apikey?name={bot.BotUserName}");
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
string username;
|
||||||
|
if(e.QueryParams.TryGetFirst("name",out username)){
|
||||||
|
Bot bot;
|
||||||
|
if(DataBase.Bots !=null && (bot=DataBase.Bots.FindOne(ee=>ee.UserId==res.Account.Id && ee.BotUserName==username))!=null)
|
||||||
|
{
|
||||||
|
string key= File.ReadAllText(getPath("apikey/index.html")).Replace("{API_KEY}",bot.ApiKey);
|
||||||
|
await e.SendTextAsync(key);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("Unauthorized user or no bot with specified username");
|
||||||
|
});
|
||||||
|
svr.Add("/user",async(e)=>{
|
||||||
|
|
||||||
|
if(DataBase.Accounts ==null) {
|
||||||
|
await e.SendTextAsync("DataBase error: Accounts is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Account account=null;
|
||||||
|
string username;
|
||||||
|
if(e.QueryParams.TryGetFirst("name",out username))
|
||||||
|
{
|
||||||
|
|
||||||
|
account=DataBase.Accounts.FindOne(ee=>ee.Username==username);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName=res.Account.Username;
|
||||||
|
long accountUserId =res.Account.Id;
|
||||||
|
if(account == null)
|
||||||
|
{
|
||||||
|
account = res.Account;
|
||||||
|
}
|
||||||
|
object logged_in=new{
|
||||||
|
Name=WebUtility.HtmlEncode(accountName),
|
||||||
|
Url=$"./user?name={WebUtility.UrlEncode(accountUserName)}",
|
||||||
|
Notloggedin=false,
|
||||||
|
Loggedin=true,
|
||||||
|
Uname=account.Username,
|
||||||
|
Loggedinme=account.Id == res.Account.Id,
|
||||||
|
Loggedinnotme=account.Id != res.Account.Id,
|
||||||
|
Othername=WebUtility.HtmlEncode(account.Name),
|
||||||
|
Opt=UserManagement.GetUserOptions(account)
|
||||||
|
};
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_user.Render(logged_in));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(account == null) {
|
||||||
|
|
||||||
|
await e.SendTextAsync("No Account");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_user.Render(new{
|
||||||
|
Url="./login",
|
||||||
|
Name="Login",
|
||||||
|
Loggedinnotme=false,
|
||||||
|
Loggedinme=false,
|
||||||
|
Loggedin=false,
|
||||||
|
Notloggedin=true,
|
||||||
|
Uname=account.Username,
|
||||||
|
Othername=WebUtility.HtmlEncode(account.Name)
|
||||||
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
//await e.SendFileAsync(getPath("user/index.html"));
|
||||||
|
});
|
||||||
|
svr.Add("/api/create-bot",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
e.ParseBody();
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
string botname;
|
||||||
|
string botuser;
|
||||||
|
string botdesc;
|
||||||
|
|
||||||
|
if(e.QueryParams.TryGetFirst("botname",out botname))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botuser",out botuser))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botdesc",out botdesc))
|
||||||
|
{
|
||||||
|
if(DataBase.Bots != null && Account.IsValidName(botname) && Account.IsValidUserName(botuser) && Account.IsValidName(botdesc)){
|
||||||
|
var bot= DataBase.Bots.FindOne(ee=>ee.BotUserName == botuser && res.Account.Id == ee.UserId);
|
||||||
|
if(bot == null)
|
||||||
|
{
|
||||||
|
Bot b=new Bot();
|
||||||
|
b.UserId= res.Account.Id;
|
||||||
|
b.BotUserName=botuser;
|
||||||
|
b.BotDescription=botdesc;
|
||||||
|
b.BotName = botname;
|
||||||
|
b.ApiKey = Account.GetNewSalt();
|
||||||
|
b.Permissions.SetPermissionsFromForm(e);
|
||||||
|
DataBase.Bots.Insert(b);
|
||||||
|
await e.SendRedirectAsync($"/apikey?name={b.BotUserName}");
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
bot.Permissions.SetPermissionsFromForm(e);
|
||||||
|
DataBase.Bots.Update(bot);
|
||||||
|
await e.SendRedirectAsync($"/apikey?name={bot.BotUserName}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},"POST");
|
||||||
|
|
||||||
|
svr.Add("/create-bot-permissions",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
e.ParseBody();
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
//get botname botuser botdesc
|
||||||
|
string botname;
|
||||||
|
string botuser;
|
||||||
|
string botdesc;
|
||||||
|
if(e.QueryParams.TryGetFirst("botname",out botname))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botuser",out botuser))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("botdesc",out botdesc))
|
||||||
|
{
|
||||||
|
if(DataBase.Bots != null){
|
||||||
|
var bot= DataBase.Bots.FindOne(ee=>ee.BotUserName == botuser && res.Account.Id == ee.UserId);
|
||||||
|
|
||||||
|
if(bot == null && Account.IsValidName(botname) && Account.IsValidUserName(botuser) && Account.IsValidName(botdesc))
|
||||||
|
{
|
||||||
|
string page0=File.ReadAllText(getPath("create-bot-permissions/index.html")).Replace("{BOT_DESC}",botdesc).Replace("{BOT_USER}",botuser).Replace("{BOT_NAME}",botname);
|
||||||
|
await e.SendTextAsync(page0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
await e.SendTextAsync("Either your not logged in or post error");
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/create-bot",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath("create-bot/index.html"));
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/api/user-setting",async(e)=>{
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
await e.SendJsonAsync(UserManagement.GetUserOptions(res.Account));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/api/user-setting",async(e)=>{
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
var opt=UserManagement.GetUserOptions(res.Account);
|
||||||
|
UserManagement.SetUserOptions(opt,e);
|
||||||
|
await e.SendRedirectAsync($"/user?name={res.Account.Username}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},"POST");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
svr.Add("/",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var mymsg=new List<MyMessage>();
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,null))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Mine=true;
|
||||||
|
mymsg.Add(msg0);
|
||||||
|
}
|
||||||
|
var users = new List<UserAccount>();
|
||||||
|
|
||||||
|
foreach(var bot in UserManagement.GetMyBots(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = $"me-{bot.BotUserName}";
|
||||||
|
usr.Text = WebUtility.HtmlEncode($"{bot.BotName} (My Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0 =new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Mine = msg.IsToBot;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
}
|
||||||
|
foreach(var otherUser in UserManagement.GetUserWithMe(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = otherUser.Username;
|
||||||
|
|
||||||
|
usr.Text = WebUtility.HtmlEncode(otherUser.Name);
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
foreach(var bot in UserManagement.GetBotsWithMe(res.Account,otherUser))
|
||||||
|
{
|
||||||
|
var usr0 = new UserAccount();
|
||||||
|
usr0.Hash = $"{otherUser.Username}-{bot.BotUserName}";
|
||||||
|
usr0.Text = WebUtility.HtmlDecode($"{bot.BotName} ({otherUser.Name}'s Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
usr0.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName =res.Account.Username;
|
||||||
|
object logged_in=new{
|
||||||
|
Name=WebUtility.HtmlEncode(accountName),
|
||||||
|
Url=$"./user?name={accountUserName}",
|
||||||
|
Loggedin=true,
|
||||||
|
Mymessages=mymsg,
|
||||||
|
Users=users
|
||||||
|
};
|
||||||
|
await e.SendTextAsync(template_index.Render(logged_in));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}catch (Exception ex){
|
||||||
|
Console.WriteLine(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_index.Render(new{
|
||||||
|
Url="./login",
|
||||||
|
Name="Login",
|
||||||
|
Loggedin=false
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
svr.Add("/api/new-ota.txt",async(e)=>{
|
||||||
|
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
DateTime expire=DateTime.Now.AddYears(1);
|
||||||
|
if(e.QueryParams.TryGetFirst("expires",out cookie_txt))
|
||||||
|
{
|
||||||
|
long exp;
|
||||||
|
if(long.TryParse(cookie_txt,out exp))
|
||||||
|
{
|
||||||
|
expire=DateTimeOffset.FromUnixTimeSeconds(exp).DateTime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string session=UserManagement.CreateAnotherSession(cookie[1],expire);
|
||||||
|
if(!string.IsNullOrWhiteSpace(session))
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
await e.SendTextAsync(UserManagement.CreateOTA(session),"text/plain");return;
|
||||||
|
}
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.StatusCode=401;
|
||||||
|
await e.WriteHeadersAsync();
|
||||||
|
|
||||||
|
});
|
||||||
|
svr.Add("/device-qr",async(e)=>{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName =res.Account.Username;
|
||||||
|
//string? session=UserManagement.CreateAnotherSession(cookie[1],DateTime.Now.AddYears(1));
|
||||||
|
/*
|
||||||
|
Console.WriteLine($"{e.Host}#{session}");
|
||||||
|
*/
|
||||||
|
|
||||||
|
object logged_in=new{
|
||||||
|
Name=WebUtility.HtmlEncode(accountName),
|
||||||
|
Url=$"./user?name={WebUtility.UrlEncode(accountUserName)}",
|
||||||
|
Loggedin=true,
|
||||||
|
};
|
||||||
|
await e.SendTextAsync(template_qr.Render(logged_in));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
await e.SendTextAsync(template_qr.Render(new{
|
||||||
|
Url="./login",
|
||||||
|
Name="Login",
|
||||||
|
Loggedin=false
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
svr.Add("/api/state.json",async(e)=>{
|
||||||
|
try{
|
||||||
|
string cookie_txt;
|
||||||
|
if(e.RequestHeaders.TryGetFirst("Cookie",out cookie_txt))
|
||||||
|
{
|
||||||
|
string[] cookie=cookie_txt.Split(new char[]{'='},2);
|
||||||
|
if(cookie.Length == 2 && !string.IsNullOrWhiteSpace(cookie[0]) && cookie[0].Equals("Chatr-Session") && !string.IsNullOrWhiteSpace(cookie[1]))
|
||||||
|
{
|
||||||
|
var res=UserManagement.Authenticate(cookie[1]);
|
||||||
|
if(res.Success && res.Account != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
var mymsg=new List<MyMessage>();
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,null))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Mine=true;
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
mymsg.Add(msg0);
|
||||||
|
}
|
||||||
|
var users = new List<UserAccount>();
|
||||||
|
|
||||||
|
foreach(var bot in UserManagement.GetMyBots(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = $"me-{bot.BotUserName}";
|
||||||
|
usr.Text = WebUtility.HtmlEncode($"{bot.BotName} (My Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,null,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0 =new MyMessage();
|
||||||
|
msg0.Mine = msg.IsToBot;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
}
|
||||||
|
foreach(var otherUser in UserManagement.GetUserWithMe(res.Account))
|
||||||
|
{
|
||||||
|
var usr=new UserAccount();
|
||||||
|
usr.Hash = otherUser.Username;
|
||||||
|
|
||||||
|
usr.Text = WebUtility.HtmlEncode(otherUser.Name);
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
usr.Messages.Add(msg0);
|
||||||
|
|
||||||
|
}
|
||||||
|
users.Add(usr);
|
||||||
|
foreach(var bot in UserManagement.GetBotsWithMe(res.Account,otherUser))
|
||||||
|
{
|
||||||
|
var usr0 = new UserAccount();
|
||||||
|
usr0.Hash = $"{otherUser.Username}-{bot.BotUserName}";
|
||||||
|
usr0.Text = WebUtility.HtmlDecode($"{bot.BotName} ({otherUser.Name}'s Bot)");
|
||||||
|
|
||||||
|
foreach(var msg in UserManagement.GetMessages(res.Account,otherUser.Username,bot.BotUserName))
|
||||||
|
{
|
||||||
|
var msg0=new MyMessage();
|
||||||
|
msg0.Mine = msg.SourceUserId == res.Account.Id;
|
||||||
|
msg0.Body = WebUtility.HtmlEncode(msg.Content);
|
||||||
|
msg0.Time=msg.CreationTime.ToString("R");
|
||||||
|
|
||||||
|
usr0.Messages.Add(msg0);
|
||||||
|
}
|
||||||
|
users.Add(usr0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
string accountName=res.Account.Name;
|
||||||
|
string accountUserName =res.Account.Username;
|
||||||
|
object logged_in=new{
|
||||||
|
Email=res.Account.Email,
|
||||||
|
Name=accountName,
|
||||||
|
Url=$"/user?name={accountUserName}",
|
||||||
|
Loggedin=true,
|
||||||
|
Mymessages=mymsg,
|
||||||
|
Users=users
|
||||||
|
};
|
||||||
|
Console.WriteLine("Alternate HI");
|
||||||
|
await e.SendJsonAsync(logged_in);
|
||||||
|
Console.WriteLine("SENT");
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}catch (Exception ex){
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
await e.SendJsonAsync(new{
|
||||||
|
Loggedin=false
|
||||||
|
});
|
||||||
|
|
||||||
|
},"GET");
|
||||||
|
svr.Add($"/login",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath("login/index.html"));
|
||||||
|
});
|
||||||
|
svr.Add($"/signup",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath("signup/index.html"));
|
||||||
|
});
|
||||||
|
Action<string> set_url=(e2)=>{
|
||||||
|
svr.Add($"/{e2}",async(e)=>{
|
||||||
|
await e.SendFileAsync(getPath(e2));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
foreach(var f in File.ReadAllLines(getPath("files.txt")))
|
||||||
|
{
|
||||||
|
set_url(f);
|
||||||
|
}
|
||||||
|
|
||||||
|
svr.Add("/api/login",async(e)=>{
|
||||||
|
|
||||||
|
|
||||||
|
e.ParseBody();
|
||||||
|
string username;
|
||||||
|
string password;
|
||||||
|
string rememberMeStr;
|
||||||
|
bool rememberMe;
|
||||||
|
|
||||||
|
if(e.QueryParams.TryGetFirst("username",out username))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password",out password))
|
||||||
|
{
|
||||||
|
rememberMe=(e.QueryParams.TryGetFirst("remember",out rememberMeStr) && rememberMeStr == "on");
|
||||||
|
var res=UserManagement.Login(username,password,rememberMe);
|
||||||
|
if(res.Success)
|
||||||
|
{
|
||||||
|
//Set-Cookie
|
||||||
|
if(rememberMe)
|
||||||
|
{
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={res.SessionId}; Path=/; Expires={res.Expires.ToString("R")}");
|
||||||
|
|
||||||
|
}else{
|
||||||
|
e.ResponseHeaders.Add("Set-Cookie",$"{LoginResult.SessionCookieName}={res.SessionId}; Path=/");
|
||||||
|
}
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
}else{
|
||||||
|
await e.SendRedirectAsync("/err/incorrect-password.html");
|
||||||
|
|
||||||
|
//do what you do when You Have Wrong password
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},"POST");
|
||||||
|
svr.Add("/api/signup",async(e)=>{
|
||||||
|
|
||||||
|
e.ParseBody();
|
||||||
|
string email;
|
||||||
|
string name;
|
||||||
|
string username;
|
||||||
|
string password;
|
||||||
|
string confirm;
|
||||||
|
if(e.QueryParams.TryGetFirst("email",out email))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("name",out name))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("uname",out username))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password",out password))
|
||||||
|
{
|
||||||
|
if(e.QueryParams.TryGetFirst("password2",out confirm))
|
||||||
|
{
|
||||||
|
if(!string.IsNullOrWhiteSpace(name) && !string.IsNullOrWhiteSpace(username) && !string.IsNullOrWhiteSpace(password) && !string.IsNullOrWhiteSpace(confirm))
|
||||||
|
{
|
||||||
|
RegistrationResult result=UserManagement.RegisterAccount(name,username,email,password);
|
||||||
|
|
||||||
|
if(result.Success)
|
||||||
|
{
|
||||||
|
await e.SendRedirectAsync("/");
|
||||||
|
return;
|
||||||
|
}else{
|
||||||
|
object o=new{
|
||||||
|
success=false,
|
||||||
|
error=result.Error
|
||||||
|
};
|
||||||
|
e.StatusCode=400;
|
||||||
|
await e.SendJsonAsync(o);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.StatusCode=400;
|
||||||
|
object op=new{
|
||||||
|
success=false,
|
||||||
|
error="Information not filled out"
|
||||||
|
};
|
||||||
|
await e.SendJsonAsync(op);
|
||||||
|
},"POST");
|
||||||
|
|
||||||
|
MountableServer _mountable=new MountableServer(svr);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
HttpServerListener listener=new HttpServerListener(http,_mountable);
|
||||||
|
|
||||||
|
Timer t=new Timer((e)=>{
|
||||||
|
try{
|
||||||
|
DateTime d=DateTime.Now;
|
||||||
|
List<long> items=new List<long>();
|
||||||
|
if(DataBase.Sessions != null){
|
||||||
|
|
||||||
|
foreach(var s in DataBase.Sessions.FindAll())
|
||||||
|
{
|
||||||
|
if(s.Expires < d)
|
||||||
|
{
|
||||||
|
items.Add(s.Id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach(var item in items){
|
||||||
|
DataBase.Sessions.Delete(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}catch(Exception ex)
|
||||||
|
{
|
||||||
|
_=ex;
|
||||||
|
}
|
||||||
|
},null,0,600000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
wssv.AddWebSocketService<WSConnection>("/chatr-ws");
|
||||||
|
wssv.Start();
|
||||||
|
if(wsInit != null)
|
||||||
|
{
|
||||||
|
wsInit();
|
||||||
|
}
|
||||||
|
|
||||||
|
await listener.ListenAsync(token.Token);
|
||||||
|
wssv.Stop();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
|
||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2015 Google Inc
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
All code in any directories or sub-directories that end with *.html or
|
||||||
|
*.css is licensed under the Creative Commons Attribution International
|
||||||
|
4.0 License, which full text can be found here:
|
||||||
|
https://creativecommons.org/licenses/by/4.0/legalcode.
|
||||||
|
|
||||||
|
As an exception to this license, all html or css that is generated by
|
||||||
|
the software at the direction of the user is copyright the user. The
|
||||||
|
user has full ownership and control over such content, including
|
||||||
|
whether and how they wish to license it.
|
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Binary file not shown.
After Width: | Height: | Size: 41 KiB |
|
@ -0,0 +1,22 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login</title>
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="center-screen">
|
||||||
|
<!-- Simple Textfield -->
|
||||||
|
<h1>Your Api Key Is</h1>
|
||||||
|
<p>{API_KEY}</p>
|
||||||
|
</div>
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
|
@ -0,0 +1,72 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login</title>
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form class="center-screen" action="./api/create-bot" method="POST">
|
||||||
|
<!-- Simple Textfield -->
|
||||||
|
<h1>Permissions</h1>
|
||||||
|
<input type="hidden" name="botuser" value="{BOT_USER}">
|
||||||
|
|
||||||
|
<input type="hidden" name="botname" value="{BOT_NAME}">
|
||||||
|
<input type="hidden" name="botdesc" value="{BOT_DESC}">
|
||||||
|
<!--
|
||||||
|
CanSendMessagesToOtherPeople=GetPermissionValue(ctx,"other_people");
|
||||||
|
OthersCanReceiveMessages=GetPermissionValue(ctx,"bot2bot");
|
||||||
|
CanSendNotifications=GetPermissionValue(ctx,"notification");
|
||||||
|
CanGetFromShareTarget=GetPermissionValue(ctx,"share_target");
|
||||||
|
CanGetMessages=GetPermissionValue(ctx,"recv_msg");
|
||||||
|
-->
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-1">
|
||||||
|
<input type="checkbox" name="other_people" id="checkbox-1" class="mdl-checkbox__input" checked>
|
||||||
|
<span class="mdl-checkbox__label">Can Send Messages To Other People</span>
|
||||||
|
</label>
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-2">
|
||||||
|
<input type="checkbox" name="bot2bot" id="checkbox-2" class="mdl-checkbox__input" checked>
|
||||||
|
<span class="mdl-checkbox__label">Can Send To Other Bots</span>
|
||||||
|
</label>
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-3">
|
||||||
|
<input type="checkbox" name="notification" id="checkbox-3" class="mdl-checkbox__input" checked>
|
||||||
|
<span class="mdl-checkbox__label">Can Send Notifications</span>
|
||||||
|
</label>
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-4">
|
||||||
|
<input type="checkbox" name="share_target" id="checkbox-4" class="mdl-checkbox__input" checked>
|
||||||
|
<span class="mdl-checkbox__label">Can Handle Share Target</span>
|
||||||
|
</label>
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-5">
|
||||||
|
<input type="checkbox" name="recv_msg" id="checkbox-5" class="mdl-checkbox__input" checked>
|
||||||
|
<span class="mdl-checkbox__label">Receive Messages</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<div class="mdl-tooltip" for="checkbox-1">
|
||||||
|
If checked this bot can send messages to other people
|
||||||
|
</div>
|
||||||
|
<div class="mdl-tooltip" for="checkbox-2">
|
||||||
|
If checked this bot can send messages to other bots
|
||||||
|
</div>
|
||||||
|
<div class="mdl-tooltip" for="checkbox-3">
|
||||||
|
If checked this bot can send notifications to your device, can send to other people
|
||||||
|
</div>
|
||||||
|
<div class="mdl-tooltip" for="checkbox-4">
|
||||||
|
If checked this bot can handle share target
|
||||||
|
</div>
|
||||||
|
<div class="mdl-tooltip" for="checkbox-5">
|
||||||
|
If checked this bot can receive messages
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" value="Create Bot">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,44 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Create A Bot</title>
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form class="center-screen" action="./create-bot-permissions" method="POST">
|
||||||
|
<h1>Chatr Bot</h1>
|
||||||
|
<!-- Simple Textfield -->
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="text" name="botuser" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Bot UserName</label>
|
||||||
|
</div>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="text" name="botname" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Bot Name</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="text" name="botdesc" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Bot Description</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" value="Next">
|
||||||
|
<a href="./my-bots" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent">
|
||||||
|
My Bots
|
||||||
|
</a>
|
||||||
|
<a href="./">Cancel</a>
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,79 @@
|
||||||
|
.center-screen
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
-ms-transform: translateX(-50%) translateY(-50%);
|
||||||
|
-webkit-transform: translate(-50%,-50%);
|
||||||
|
transform: translate(-50%,-50%);
|
||||||
|
}
|
||||||
|
.center-screen2
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
top: 0%;
|
||||||
|
left: 50%;
|
||||||
|
-ms-transform: translateX(-50%) translateY(0%);
|
||||||
|
-webkit-transform: translate(-50%,0%);
|
||||||
|
transform: translate(-50%,0%);
|
||||||
|
}
|
||||||
|
.center-screen2 h1
|
||||||
|
{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.center-screen h1
|
||||||
|
{
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
#qrcode
|
||||||
|
{
|
||||||
|
position: absolute;
|
||||||
|
height: fit-content;
|
||||||
|
width: fit-content;
|
||||||
|
left: 50%;
|
||||||
|
-ms-transform: translateX(-50%) translateY(0%);
|
||||||
|
-webkit-transform: translate(-50%,0%);
|
||||||
|
transform: translate(-50%,0%);
|
||||||
|
}
|
||||||
|
|
||||||
|
ul{
|
||||||
|
list-style: none;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul li{
|
||||||
|
display:inline-block;
|
||||||
|
clear: both;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 30px;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
font-family: Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
.him{
|
||||||
|
background: #eee;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.me{
|
||||||
|
float: right;
|
||||||
|
background: #0084ff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.him + .me{
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.me + .me{
|
||||||
|
border-top-right-radius: 5px;
|
||||||
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.me:last-of-type {
|
||||||
|
border-bottom-right-radius: 30px;
|
||||||
|
}
|
||||||
|
.float-right
|
||||||
|
{
|
||||||
|
float: right;
|
||||||
|
}
|
|
@ -0,0 +1,76 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Chatr</title>
|
||||||
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Always shows a header, even in smaller screens. -->
|
||||||
|
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
|
||||||
|
<header class="mdl-layout__header">
|
||||||
|
<div class="mdl-layout__header-row">
|
||||||
|
<!-- Title -->
|
||||||
|
<span class="mdl-layout-title">Chatr</span>
|
||||||
|
<!-- Add spacer, to align navigation to the right -->
|
||||||
|
<div class="mdl-layout-spacer"></div>
|
||||||
|
<!-- Navigation. We hide it in small screens. -->
|
||||||
|
<nav class="mdl-navigation mdl-layout--large-screen-only">
|
||||||
|
|
||||||
|
<a class="mdl-navigation__link" href="{{ url }}">{{ name }}</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="mdl-layout__drawer">
|
||||||
|
<span class="mdl-layout-title">Chatr</span>
|
||||||
|
<nav class="mdl-navigation">
|
||||||
|
|
||||||
|
<a class="mdl-navigation__link" href="{{ url }}">{{ name }}</a>
|
||||||
|
<a class="mdl-navigation__link" href="./">Home</a>
|
||||||
|
{{ if loggedin }}
|
||||||
|
|
||||||
|
<a class="mdl-navigation__link" href="./api/logout">Logout</a>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<main class="mdl-layout__content">
|
||||||
|
<div class="page-content">
|
||||||
|
<div class="center-screen2">
|
||||||
|
<h1>Login Using QR Code</h1>
|
||||||
|
{{ if loggedin }}
|
||||||
|
<p>Your password won't be shared with app, cause we don't remember it</p>
|
||||||
|
|
||||||
|
<div id="qrcode"></div>
|
||||||
|
<br>
|
||||||
|
{{ else }}
|
||||||
|
<p>Please Login <a href="./login">Login</a> Or <a href="./signup">Sign Up</a></p>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
{{ if loggedin }}
|
||||||
|
<script src="./js/qrcode.min.js"></script>
|
||||||
|
<script>
|
||||||
|
fetch(`./api/new-ota.txt${window.location.search}`).then(e=>e.text()).then(e=>{
|
||||||
|
var qrcode = new QRCode(document.getElementById("qrcode"), {
|
||||||
|
width : 256,
|
||||||
|
height : 256,
|
||||||
|
text: window.location.href.replace('/device-qr',`/onetimeauth?key=${e}`),
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
{{ end }}
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,16 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Wrong Password</title>
|
||||||
|
<link rel="manifest" href="../site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="../favicon.ico">
|
||||||
|
<meta http-equiv="refresh" content="5; URL='./'" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Wrong Password</h1>
|
||||||
|
<a href="../login">Try Again</a>
|
||||||
|
</body>
|
||||||
|
</html>
|
Binary file not shown.
After Width: | Height: | Size: 710 B |
Binary file not shown.
After Width: | Height: | Size: 1.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
|
@ -0,0 +1,20 @@
|
||||||
|
bower.json
|
||||||
|
LICENSE
|
||||||
|
css/material.min.css
|
||||||
|
js/material.min.js
|
||||||
|
css/styles.css
|
||||||
|
js/mainscript.js
|
||||||
|
js/worker.js
|
||||||
|
js/serviceworker.js
|
||||||
|
js/material.min.js.map
|
||||||
|
css/material.min.css.map
|
||||||
|
js/jquery.min.js
|
||||||
|
js/qrcode.min.js
|
||||||
|
favicon.ico
|
||||||
|
android-chrome-192x192.png
|
||||||
|
android-chrome-512x512.png
|
||||||
|
apple-touch-icon.png
|
||||||
|
favicon-16x16.png
|
||||||
|
favicon-32x32.png
|
||||||
|
favicon.icon
|
||||||
|
site.webmanifest
|
|
@ -0,0 +1,154 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Chatr</title>
|
||||||
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Always shows a header, even in smaller screens. -->
|
||||||
|
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
|
||||||
|
<header class="mdl-layout__header">
|
||||||
|
<div class="mdl-layout__header-row">
|
||||||
|
<!-- Title -->
|
||||||
|
<span class="mdl-layout-title">Chatr</span>
|
||||||
|
<!-- Add spacer, to align navigation to the right -->
|
||||||
|
<div class="mdl-layout-spacer"></div>
|
||||||
|
<!-- Navigation. We hide it in small screens. -->
|
||||||
|
<nav class="mdl-navigation mdl-layout--large-screen-only">
|
||||||
|
|
||||||
|
<a class="mdl-navigation__link" href="{{ url }}">{{ name }}</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<div class="mdl-layout__tab-bar mdl-js-ripple-effect" id="_links">
|
||||||
|
<a href="#me" id="link-me" class="mdl-layout__tab is-active">Me</a>
|
||||||
|
<!--<a href="#tytd-bot" class="mdl-layout__tab">TYTD (Bot)</a>-->
|
||||||
|
{{ for user in users }}
|
||||||
|
<a href="#{{ user.hash }}" id="link-{{ user.hash }}" class="mdl-layout__tab">{{ user.text }} </a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="mdl-layout__drawer">
|
||||||
|
<span class="mdl-layout-title">Chatr</span>
|
||||||
|
<nav class="mdl-navigation">
|
||||||
|
|
||||||
|
<a class="mdl-navigation__link" href="{{ url }}">{{ name }}</a>
|
||||||
|
|
||||||
|
{{ if loggedin }}
|
||||||
|
<a class="mdl-navigation__link" href="./create-bot">Create Bot</a>
|
||||||
|
<a class="mdl-navigation__link" href="./device-qr">Login With QR Code</a>
|
||||||
|
<a class="mdl-navigation__link" href="./api/logout">Logout</a>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
<a class="mdl-navigation__link" href="./" onclick="return setup_notifications()">Ask for Notifications</a>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<main class="mdl-layout__content">
|
||||||
|
<section class="mdl-layout__tab-panel is-active" id="me">
|
||||||
|
<div class="page-content">
|
||||||
|
<ul id="messages-me">
|
||||||
|
{{ for message in mymessages }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<li class="me">{{ message.body }}</li>
|
||||||
|
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{ for user in users }}
|
||||||
|
<section class="mdl-layout__tab-panel" id="{{ user.hash }}">
|
||||||
|
<div class="page-content">
|
||||||
|
<ul id="messages-{{ user.hash }}">
|
||||||
|
{{ for message in user.messages }}
|
||||||
|
|
||||||
|
|
||||||
|
{{ if message.mine }}
|
||||||
|
<li class="me">{{ message.body }}</li>
|
||||||
|
{{ else }}
|
||||||
|
<li class="him">{{ message.body }}</li>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
<dialog class="mdl-dialog" id="clear_all_dlg">
|
||||||
|
<h4 class="mdl-dialog__title">Delete all messages with this user/bot?</h4>
|
||||||
|
<div class="mdl-dialog__content">
|
||||||
|
<p>
|
||||||
|
NOTE this will happen for the other party too
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="mdl-dialog__actions">
|
||||||
|
<button type="button" onclick="clear_all_confirm()" class="mdl-button mdl-button--accent close">Yes</button>
|
||||||
|
<button type="button" onclick="clear_all_cancel()" class="mdl-button close">No</button>
|
||||||
|
</div>
|
||||||
|
</dialog>
|
||||||
|
</main>
|
||||||
|
<footer>
|
||||||
|
<div class="float-right">
|
||||||
|
|
||||||
|
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="text" id="msg_tb">
|
||||||
|
<label class="mdl-textfield__label" for="msg_tb">Message...</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick="send_msg_to()" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
|
||||||
|
<i class="material-icons">send</i>
|
||||||
|
</button>
|
||||||
|
<button id="moreBtn" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
|
||||||
|
<i class="material-icons">more_vert</i>
|
||||||
|
</button>
|
||||||
|
<div id="moreBtns" hidden>
|
||||||
|
|
||||||
|
<button onclick="clear_all()" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
|
||||||
|
<i class="material-icons">clear_all</i>
|
||||||
|
</button>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
|
||||||
|
<button onclick="upload_messages()" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
|
||||||
|
<i class="material-icons">file_open</i>
|
||||||
|
</button>
|
||||||
|
<form method="POST" action="./api/upload_messages" hidden>
|
||||||
|
<input type="file" name="file">
|
||||||
|
|
||||||
|
</form>
|
||||||
|
<button onclick="download_messages()" class="mdl-button mdl-js-button mdl-button--icon mdl-button--colored">
|
||||||
|
<i class="material-icons">save</i>
|
||||||
|
</button>-->
|
||||||
|
<br>
|
||||||
|
<form id="upload_file" method="POST" action="./api/uploadUser" hidden>
|
||||||
|
<input type="hidden" name="uid" id="uid" value="me">
|
||||||
|
<input type="submit" value="Send">
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</div>
|
||||||
|
<script src="./js/mainscript.js"></script>
|
||||||
|
<script defer>
|
||||||
|
var pgs = ['me'{{ for user in users }} ,'{{ user.hash }}' {{end }}];
|
||||||
|
var curUser='me';
|
||||||
|
|
||||||
|
init_ws();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,255 @@
|
||||||
|
|
||||||
|
function toggle(elmt1,elmt2)
|
||||||
|
{
|
||||||
|
if(elmt1.classList.contains('mdl-button--accent'))
|
||||||
|
{
|
||||||
|
elmt2.hidden=true;
|
||||||
|
elmt1.classList.remove('mdl-button--accent');
|
||||||
|
elmt1.classList.add('mdl-button--colored');
|
||||||
|
}else{
|
||||||
|
elmt2.hidden=false;
|
||||||
|
elmt1.classList.add('mdl-button--accent');
|
||||||
|
elmt1.classList.remove('mdl-button--colored');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function set_msg(usr)
|
||||||
|
{
|
||||||
|
curUser=usr.replace('#','');
|
||||||
|
}
|
||||||
|
function add_msg(msg)
|
||||||
|
{
|
||||||
|
if(!pgs.includes(msg.UserStr))
|
||||||
|
{
|
||||||
|
window.location.reload();
|
||||||
|
}else if(msg.UserStr != 'me')
|
||||||
|
{
|
||||||
|
var elmt = document.getElementById(`messages-${msg.UserStr}`);
|
||||||
|
|
||||||
|
var elmnt0=document.createElement('li');
|
||||||
|
elmnt0.classList.add('him');
|
||||||
|
elmnt0.innerText=msg.Content;
|
||||||
|
|
||||||
|
elmt.appendChild(elmnt0);
|
||||||
|
elmnt0.scrollIntoView(false);
|
||||||
|
|
||||||
|
}
|
||||||
|
send_notification_basic(msg.Content,msg.Name);
|
||||||
|
//if(msg.UserStr)
|
||||||
|
}
|
||||||
|
const dialog = document.getElementById('clear_all_dlg');
|
||||||
|
function clear_all()
|
||||||
|
{
|
||||||
|
dialog.show();
|
||||||
|
}
|
||||||
|
function clear_all_cancel()
|
||||||
|
{
|
||||||
|
dialog.close();
|
||||||
|
}
|
||||||
|
function clear_all_confirm()
|
||||||
|
{
|
||||||
|
var ws_data={
|
||||||
|
PacketType: 6,
|
||||||
|
Data: {
|
||||||
|
UserStr:curUser
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ws.send(JSON.stringify(ws_data));
|
||||||
|
dialog.close();
|
||||||
|
var elmt = document.getElementById(`messages-${curUser}`);
|
||||||
|
elmt.innerHTML="";
|
||||||
|
}
|
||||||
|
function send_msg_to()
|
||||||
|
{
|
||||||
|
|
||||||
|
var tb = document.getElementById('msg_tb');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var ws_data={
|
||||||
|
PacketType: 3,
|
||||||
|
Data: {
|
||||||
|
Content:tb.value,
|
||||||
|
UserStr:curUser
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.ws.send(JSON.stringify(ws_data));
|
||||||
|
|
||||||
|
var elmt = document.getElementById(`messages-${curUser}`);
|
||||||
|
|
||||||
|
var elmnt0=document.createElement('li');
|
||||||
|
elmnt0.classList.add('me');
|
||||||
|
elmnt0.innerText=tb.value;
|
||||||
|
elmt.appendChild(elmnt0);
|
||||||
|
|
||||||
|
elmnt0.scrollIntoView(false);
|
||||||
|
|
||||||
|
tb.value="";
|
||||||
|
|
||||||
|
}
|
||||||
|
function setup_notifications()
|
||||||
|
{
|
||||||
|
Notification.requestPermission();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function send_notification_basic(text,name)
|
||||||
|
{
|
||||||
|
if(Notification.permission === "granted"){
|
||||||
|
const msg = new Notification(`Chatr Message From: ${name}`,{
|
||||||
|
body: text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handle_notification_event(props)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
props.BotNotificationId, props.BotOwnerUserName, props.BotUserString, props.Button send to server
|
||||||
|
props.Url, props.Type for client
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(props.Type === 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(props.Type === 1 || props.Type === 2)
|
||||||
|
{
|
||||||
|
window.open(props.Url,"_blank")
|
||||||
|
|
||||||
|
}
|
||||||
|
if(props.Type > 2)
|
||||||
|
{
|
||||||
|
window.ws.send(JSON.stringify({
|
||||||
|
BotUserString: props.BotUserString,
|
||||||
|
BotNotificationId: props.BotNotificationId,
|
||||||
|
SourceUserName: props.BotOwnerUserName,
|
||||||
|
Button:props.Button
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function send_notification_bot(msg)
|
||||||
|
{
|
||||||
|
if(Notification.permission === "granted")
|
||||||
|
{
|
||||||
|
var actions = [];
|
||||||
|
msg.NotificationButtons.forEach((e)=>{
|
||||||
|
actions.push({
|
||||||
|
action: e,
|
||||||
|
title: e.Text
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const not = new Notification(`(${msg.BotName} (${msg.BotOwnerName}'s Bot)) ${msg.Title}`,{
|
||||||
|
body: msg.Body,
|
||||||
|
actions: actions
|
||||||
|
});
|
||||||
|
|
||||||
|
not.onclick = (e)=>{
|
||||||
|
console.log(e.action);
|
||||||
|
|
||||||
|
var url="";
|
||||||
|
var type = 0;
|
||||||
|
var buttonTxt="";
|
||||||
|
if(!e.action)
|
||||||
|
{
|
||||||
|
type=msg.OnClick.Type;
|
||||||
|
if(type === 0) return;
|
||||||
|
if(type === 1 || type === 2)
|
||||||
|
{
|
||||||
|
url = msg.OnClick.Data.Url;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
type=e.Event.Type;
|
||||||
|
if(type === 0)return;
|
||||||
|
if(type===1 || type === 2)
|
||||||
|
{
|
||||||
|
url = e.Event.Data.Url;
|
||||||
|
}
|
||||||
|
buttonTxt=e.Text;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_notification_event({
|
||||||
|
BotOwnerUserName:msg.BotOwnerUserName,
|
||||||
|
BotUserString:msg.BotUserString,
|
||||||
|
BotNotificationId:msg.BotNotificationId,
|
||||||
|
Button:buttonTxt,
|
||||||
|
Url:url,
|
||||||
|
Type:type
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_user_callback(v)
|
||||||
|
{
|
||||||
|
var elmt = document.getElementById(`link-${v}`);
|
||||||
|
elmt.onclick = function(e){
|
||||||
|
set_msg(v);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
function getCookie(name) {
|
||||||
|
const value = `; ${document.cookie}`;
|
||||||
|
const parts = value.split(`; ${name}=`);
|
||||||
|
if (parts.length === 2) return parts.pop().split(';').shift();
|
||||||
|
}
|
||||||
|
function init_ws()
|
||||||
|
{
|
||||||
|
const b1=document.getElementById('moreBtn');
|
||||||
|
const div = document.getElementById('moreBtns');
|
||||||
|
b1.onclick = (e)=>{
|
||||||
|
toggle(b1,div);
|
||||||
|
|
||||||
|
};
|
||||||
|
var j=null;
|
||||||
|
set_user_callback('me');
|
||||||
|
pgs.forEach(e=>{
|
||||||
|
set_user_callback(e);
|
||||||
|
});
|
||||||
|
fetch("./api/appconfig.json",{credentials:"same-origin"}).then(e=>e.json()).then(e=>{
|
||||||
|
|
||||||
|
var wsStr="";
|
||||||
|
//
|
||||||
|
console.log(e.WS_SAME_AS_HTTP);
|
||||||
|
if(e.WS_SAME_AS_HTTP)
|
||||||
|
{
|
||||||
|
var scheme= window.location.protocol == "http:" ? "ws" : "wss";
|
||||||
|
wsStr=`${scheme}://${window.location.host}${window.location.pathname}chatr-ws`;
|
||||||
|
}else{
|
||||||
|
var scheme = e.WSS ? 'wss' : 'ws';
|
||||||
|
wsStr=`${scheme}://${window.location.hostname}:${e.WSPort}${window.location.pathname}chatr-ws`;
|
||||||
|
}
|
||||||
|
|
||||||
|
var cses=getCookie("Chatr-Session");
|
||||||
|
|
||||||
|
if(cses != null && cses != undefined){
|
||||||
|
window.ws=new WebSocket(wsStr);
|
||||||
|
window.ws.onopen = function(e){
|
||||||
|
window.ws.send(JSON.stringify({
|
||||||
|
PacketType: 1,
|
||||||
|
Data: {
|
||||||
|
SessionId: cses
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
window.ws.onmessage=function(e){
|
||||||
|
console.log(e.data);
|
||||||
|
var jsonData=JSON.parse(e.data);
|
||||||
|
switch(jsonData.PacketType)
|
||||||
|
{
|
||||||
|
case 3:
|
||||||
|
add_msg(jsonData.Data);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
//send_notification_bot(jsonData.Data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,39 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login</title>
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form class="center-screen" action="./api/login" method="POST">
|
||||||
|
<!-- Simple Textfield -->
|
||||||
|
<h1>Chatr</h1>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="text" name="username" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Username</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="password" name="password" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Password</label>
|
||||||
|
</div><br>
|
||||||
|
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-1">
|
||||||
|
<input type="checkbox" id="checkbox-1" name="remember" class="mdl-checkbox__input" checked>
|
||||||
|
<span class="mdl-checkbox__label">Remember Me</span>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" value="Login">
|
||||||
|
<a href="./signup">Sign Up</a>
|
||||||
|
</form>
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,77 @@
|
||||||
|
{
|
||||||
|
"name": "material-design-lite",
|
||||||
|
"version": "1.3.0",
|
||||||
|
"description": "Material Design Components in CSS, JS and HTML",
|
||||||
|
"private": true,
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"author": "Google",
|
||||||
|
"repository": "google/material-design-lite",
|
||||||
|
"main": "dist/material.min.js",
|
||||||
|
"devDependencies": {
|
||||||
|
"acorn": "^4.0.3",
|
||||||
|
"babel-core": "^6.20.0",
|
||||||
|
"babel-preset-es2015": "^6.18.0",
|
||||||
|
"browser-sync": "^2.2.3",
|
||||||
|
"chai": "^3.3.0",
|
||||||
|
"chai-jquery": "^2.0.0",
|
||||||
|
"del": "^2.0.2",
|
||||||
|
"drool": "^0.4.0",
|
||||||
|
"escodegen": "^1.6.1",
|
||||||
|
"google-closure-compiler": "",
|
||||||
|
"gulp": "^3.9.0",
|
||||||
|
"gulp-autoprefixer": "^3.0.2",
|
||||||
|
"gulp-cache": "^0.4.5",
|
||||||
|
"gulp-closure-compiler": "^0.4.0",
|
||||||
|
"gulp-concat": "^2.4.1",
|
||||||
|
"gulp-connect": "^5.0.0",
|
||||||
|
"gulp-css-inline-images": "^0.1.1",
|
||||||
|
"gulp-csso": "1.0.0",
|
||||||
|
"gulp-file": "^0.3.0",
|
||||||
|
"gulp-flatten": "^0.3.1",
|
||||||
|
"gulp-front-matter": "^1.2.2",
|
||||||
|
"gulp-header": "^1.2.2",
|
||||||
|
"gulp-if": "^2.0.0",
|
||||||
|
"gulp-iife": "^0.3.0",
|
||||||
|
"gulp-imagemin": "^3.1.0",
|
||||||
|
"gulp-jscs": "^4.0.0",
|
||||||
|
"gulp-jshint": "^2.0.4",
|
||||||
|
"gulp-load-plugins": "^1.3.0",
|
||||||
|
"gulp-marked": "^1.0.0",
|
||||||
|
"gulp-mocha-phantomjs": "^0.12.0",
|
||||||
|
"gulp-open": "^2.0.0",
|
||||||
|
"gulp-rename": "^1.2.0",
|
||||||
|
"gulp-replace": "^0.5.3",
|
||||||
|
"gulp-sass": "3.0.0",
|
||||||
|
"gulp-shell": "^0.5.2",
|
||||||
|
"gulp-size": "^2.0.0",
|
||||||
|
"gulp-sourcemaps": "^2.0.1",
|
||||||
|
"gulp-subtree": "^0.1.0",
|
||||||
|
"gulp-tap": "^0.1.3",
|
||||||
|
"gulp-uglify": "^2.0.0",
|
||||||
|
"gulp-util": "^3.0.4",
|
||||||
|
"gulp-zip": "^3.0.2",
|
||||||
|
"humanize": "0.0.9",
|
||||||
|
"jquery": "^3.1.1",
|
||||||
|
"jshint": "^2.9.4",
|
||||||
|
"jshint-stylish": "^2.2.1",
|
||||||
|
"merge-stream": "^1.0.0",
|
||||||
|
"mocha": "^3.0.2",
|
||||||
|
"prismjs": "0.0.1",
|
||||||
|
"run-sequence": "^1.0.2",
|
||||||
|
"swig": "^1.4.2",
|
||||||
|
"through2": "^2.0.0",
|
||||||
|
"vinyl-paths": "^2.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12.0"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "gulp && git status | grep 'working directory clean' >/dev/null || (echo 'Please commit all changes generated by building'; exit 1)"
|
||||||
|
},
|
||||||
|
"babel": {
|
||||||
|
"only": "gulpfile.babel.js",
|
||||||
|
"presets": [
|
||||||
|
"es2015"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Sign Up</title>
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="stylesheet" href="./css/styles.css">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<form class="center-screen" action="./api/signup" method="POST">
|
||||||
|
<h1>Chatr</h1>
|
||||||
|
<!-- Simple Textfield -->
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="text" name="name" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Your Name</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="email" name="email" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Email</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="text" name="uname" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Username</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="password" name="password" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Password</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<input class="mdl-textfield__input" type="password" name="password2" id="sample1">
|
||||||
|
<label class="mdl-textfield__label" for="sample1">Confirm Password</label>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-button--colored" value="Signup">
|
||||||
|
<a href="./login">Login</a>
|
||||||
|
</form>
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,11 @@
|
||||||
|
{"name":"Tesses Chatr",
|
||||||
|
"start_url":"./","short_name":"Chatr","icons":[{"src":"./android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"./android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#1991ff","background_color":"#a0a0ff","display":"standalone","share_target":{
|
||||||
|
"action": "./share",
|
||||||
|
"method": "POST",
|
||||||
|
"enctype": "multipart/form-data",
|
||||||
|
"params":{
|
||||||
|
"title": "share-title",
|
||||||
|
"text": "share-text",
|
||||||
|
"url": "share-url"
|
||||||
|
}
|
||||||
|
}}
|
|
@ -0,0 +1,132 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Chatr User: {{ othername }}</title>
|
||||||
|
<link rel="stylesheet" href="./css/material.min.css">
|
||||||
|
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">
|
||||||
|
<link rel="manifest" href="./site.webmanifest">
|
||||||
|
<link rel="icon" type="image/x-icon" href="./favicon.ico">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- Always shows a header, even in smaller screens. -->
|
||||||
|
<div class="mdl-layout mdl-js-layout mdl-layout--fixed-header">
|
||||||
|
<header class="mdl-layout__header">
|
||||||
|
<div class="mdl-layout__header-row">
|
||||||
|
<!-- Title -->
|
||||||
|
<span class="mdl-layout-title">Chatr User: {{ othername }}</span>
|
||||||
|
<!-- Add spacer, to align navigation to the right -->
|
||||||
|
<div class="mdl-layout-spacer"></div>
|
||||||
|
<!-- Navigation. We hide it in small screens. -->
|
||||||
|
<nav class="mdl-navigation mdl-layout--large-screen-only">
|
||||||
|
<a class="mdl-navigation__link" href="./">Home</a> <a class="mdl-navigation__link" href="{{ url }}">{{ name }}</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
<div class="mdl-layout__drawer">
|
||||||
|
<span class="mdl-layout-title">Chatr</span>
|
||||||
|
<nav class="mdl-navigation">
|
||||||
|
|
||||||
|
<a class="mdl-navigation__link" href="{{ url }}">{{ name }}</a>
|
||||||
|
<a class="mdl-navigation__link" href="./">Home</a>
|
||||||
|
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
<main class="mdl-layout__content">
|
||||||
|
<div class="page-content">
|
||||||
|
<h1>{{ othername }}</h1>
|
||||||
|
|
||||||
|
|
||||||
|
{{ if notloggedin }}
|
||||||
|
<h3>Send Anonymous Message</h3>
|
||||||
|
<form action="./api/msg" method="POST">
|
||||||
|
<input type="hidden" name="message_to" value="{{ uname }}">
|
||||||
|
|
||||||
|
<input type="hidden" name="redirect_to" value="/user?name={{ uname }}">
|
||||||
|
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<textarea class="mdl-textfield__input" type="text" rows= "6" id="sample5" name="body" ></textarea>
|
||||||
|
<label class="mdl-textfield__label" for="sample5">Message Body</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" value="Send">
|
||||||
|
</form>
|
||||||
|
{{ else if loggedinnotme }}
|
||||||
|
<h3>Send Message</h3>
|
||||||
|
<form action="./api/msg" method="POST">
|
||||||
|
<input type="hidden" name="message_to" value="{{ uname }}">
|
||||||
|
<input type="hidden" name="redirect_to" value="/user?name={{ uname }}">
|
||||||
|
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<textarea class="mdl-textfield__input" type="text" rows= "6" id="sample5" name="body" ></textarea>
|
||||||
|
<label class="mdl-textfield__label" for="sample5">Message Body</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" value="Send">
|
||||||
|
</form>
|
||||||
|
{{ else }}
|
||||||
|
<h3>Send Message To Self</h3>
|
||||||
|
<form action="./api/msg" method="POST">
|
||||||
|
<input type="hidden" name="message_to" value="{{ uname }}">
|
||||||
|
<input type="hidden" name="redirect_to" value="/user?name={{ uname }}">
|
||||||
|
|
||||||
|
<div class="mdl-textfield mdl-js-textfield">
|
||||||
|
<textarea class="mdl-textfield__input" type="text" rows= "6" id="sample5" name="body" ></textarea>
|
||||||
|
<label class="mdl-textfield__label" for="sample5">Message Body</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" value="Send">
|
||||||
|
</form>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
{{ if loggedinme }}
|
||||||
|
<h3>Options</h3>
|
||||||
|
<a class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" href="./api/logout">Logout</a>
|
||||||
|
<form action="./api/user-setting" method="POST">
|
||||||
|
<h3>User Settings</h3>
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-1">
|
||||||
|
<input type="checkbox" id="checkbox-1" class="mdl-checkbox__input" name="hide_bots" {{ opt.hidebotschecked }}>
|
||||||
|
<span class="mdl-checkbox__label">Hide Bots</span>
|
||||||
|
</label>
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-2">
|
||||||
|
<input type="checkbox" id="checkbox-2" class="mdl-checkbox__input" name="show_user" {{opt.showuserchecked}}>
|
||||||
|
<span class="mdl-checkbox__label">Show User To Everybody</span>
|
||||||
|
</label>
|
||||||
|
<h3>Show/Hide Users</h3>
|
||||||
|
{{ for user in opt.users }}
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-3">
|
||||||
|
<input type="checkbox" id="checkbox-3" class="mdl-checkbox__input" name="user{{ user.hash }}" {{ user.shown }}>
|
||||||
|
<span class="mdl-checkbox__label">{{ user.text }}</span>
|
||||||
|
</label>
|
||||||
|
{{ end }}
|
||||||
|
<h3>Show/Hide Bots</h3>
|
||||||
|
{{ for bot in opt.bots }}
|
||||||
|
<label class="mdl-checkbox mdl-js-checkbox mdl-js-ripple-effect" for="checkbox-3">
|
||||||
|
<input type="checkbox" id="checkbox-3" class="mdl-checkbox__input" name="bot{{ bot.hash }}" {{ bot.shown }}>
|
||||||
|
<span class="mdl-checkbox__label">{{ bot.text }}</span>
|
||||||
|
</label>
|
||||||
|
{{ end }}
|
||||||
|
<input type="submit" class="mdl-button mdl-js-button mdl-button--raised mdl-js-ripple-effect mdl-button--accent" value="Save">
|
||||||
|
</form>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="./js/material.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue