amki
6 years ago
63 changed files with 13486 additions and 0 deletions
@ -0,0 +1,25 @@ |
|||||
|
|
||||
|
Microsoft Visual Studio Solution File, Format Version 12.00 |
||||
|
# Visual Studio Version 16 |
||||
|
VisualStudioVersion = 16.0.28729.10 |
||||
|
MinimumVisualStudioVersion = 10.0.40219.1 |
||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DiscoBot", "DiscoBot\DiscoBot.csproj", "{8667F2ED-08B2-47B6-B7B4-2F78B5FD2AF8}" |
||||
|
EndProject |
||||
|
Global |
||||
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution |
||||
|
Debug|Any CPU = Debug|Any CPU |
||||
|
Release|Any CPU = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ProjectConfigurationPlatforms) = postSolution |
||||
|
{8667F2ED-08B2-47B6-B7B4-2F78B5FD2AF8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU |
||||
|
{8667F2ED-08B2-47B6-B7B4-2F78B5FD2AF8}.Debug|Any CPU.Build.0 = Debug|Any CPU |
||||
|
{8667F2ED-08B2-47B6-B7B4-2F78B5FD2AF8}.Release|Any CPU.ActiveCfg = Release|Any CPU |
||||
|
{8667F2ED-08B2-47B6-B7B4-2F78B5FD2AF8}.Release|Any CPU.Build.0 = Release|Any CPU |
||||
|
EndGlobalSection |
||||
|
GlobalSection(SolutionProperties) = preSolution |
||||
|
HideSolutionNode = FALSE |
||||
|
EndGlobalSection |
||||
|
GlobalSection(ExtensibilityGlobals) = postSolution |
||||
|
SolutionGuid = {60640959-F628-4073-A76A-86BE86EBAF92} |
||||
|
EndGlobalSection |
||||
|
EndGlobal |
@ -0,0 +1,231 @@ |
|||||
|
## Ignore Visual Studio temporary files, build results, and |
||||
|
## files generated by popular Visual Studio add-ons. |
||||
|
|
||||
|
# User-specific files |
||||
|
*.suo |
||||
|
*.user |
||||
|
*.userosscache |
||||
|
*.sln.docstates |
||||
|
|
||||
|
# User-specific files (MonoDevelop/Xamarin Studio) |
||||
|
*.userprefs |
||||
|
|
||||
|
# Build results |
||||
|
[Dd]ebug/ |
||||
|
[Dd]ebugPublic/ |
||||
|
[Rr]elease/ |
||||
|
[Rr]eleases/ |
||||
|
x64/ |
||||
|
x86/ |
||||
|
build/ |
||||
|
bld/ |
||||
|
bin/ |
||||
|
Bin/ |
||||
|
obj/ |
||||
|
Obj/ |
||||
|
|
||||
|
# Visual Studio 2015 cache/options directory |
||||
|
.vs/ |
||||
|
|
||||
|
# MSTest test Results |
||||
|
[Tt]est[Rr]esult*/ |
||||
|
[Bb]uild[Ll]og.* |
||||
|
|
||||
|
# NUNIT |
||||
|
*.VisualState.xml |
||||
|
TestResult.xml |
||||
|
|
||||
|
# Build Results of an ATL Project |
||||
|
[Dd]ebugPS/ |
||||
|
[Rr]eleasePS/ |
||||
|
dlldata.c |
||||
|
|
||||
|
*_i.c |
||||
|
*_p.c |
||||
|
*_i.h |
||||
|
*.ilk |
||||
|
*.meta |
||||
|
*.obj |
||||
|
*.pch |
||||
|
*.pdb |
||||
|
*.pgc |
||||
|
*.pgd |
||||
|
*.rsp |
||||
|
*.sbr |
||||
|
*.tlb |
||||
|
*.tli |
||||
|
*.tlh |
||||
|
*.tmp |
||||
|
*.tmp_proj |
||||
|
*.log |
||||
|
*.vspscc |
||||
|
*.vssscc |
||||
|
.builds |
||||
|
*.pidb |
||||
|
*.svclog |
||||
|
*.scc |
||||
|
|
||||
|
# Chutzpah Test files |
||||
|
_Chutzpah* |
||||
|
|
||||
|
# Visual C++ cache files |
||||
|
ipch/ |
||||
|
*.aps |
||||
|
*.ncb |
||||
|
*.opendb |
||||
|
*.opensdf |
||||
|
*.sdf |
||||
|
*.cachefile |
||||
|
|
||||
|
# Visual Studio profiler |
||||
|
*.psess |
||||
|
*.vsp |
||||
|
*.vspx |
||||
|
*.sap |
||||
|
|
||||
|
# TFS 2012 Local Workspace |
||||
|
$tf/ |
||||
|
|
||||
|
# Guidance Automation Toolkit |
||||
|
*.gpState |
||||
|
|
||||
|
# ReSharper is a .NET coding add-in |
||||
|
_ReSharper*/ |
||||
|
*.[Rr]e[Ss]harper |
||||
|
*.DotSettings.user |
||||
|
|
||||
|
# JustCode is a .NET coding add-in |
||||
|
.JustCode |
||||
|
|
||||
|
# TeamCity is a build add-in |
||||
|
_TeamCity* |
||||
|
|
||||
|
# DotCover is a Code Coverage Tool |
||||
|
*.dotCover |
||||
|
|
||||
|
# 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 |
||||
|
# TODO: 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 |
||||
|
|
||||
|
# NuGet Packages |
||||
|
*.nupkg |
||||
|
# The packages folder can be ignored because of Package Restore |
||||
|
**/packages/* |
||||
|
# except build/, which is used as an MSBuild target. |
||||
|
!**/packages/build/ |
||||
|
# Uncomment if necessary however generally it will be regenerated when needed |
||||
|
#!**/packages/repositories.config |
||||
|
|
||||
|
# Microsoft Azure Build Output |
||||
|
csx/ |
||||
|
*.build.csdef |
||||
|
|
||||
|
# Microsoft Azure Emulator |
||||
|
ecf/ |
||||
|
rcf/ |
||||
|
|
||||
|
# Microsoft Azure ApplicationInsights config file |
||||
|
ApplicationInsights.config |
||||
|
|
||||
|
# Windows Store app package directory |
||||
|
AppPackages/ |
||||
|
BundleArtifacts/ |
||||
|
|
||||
|
# 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 |
||||
|
*.pfx |
||||
|
*.publishsettings |
||||
|
orleans.codegen.cs |
||||
|
|
||||
|
/node_modules |
||||
|
|
||||
|
# 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 |
||||
|
|
||||
|
# SQL Server files |
||||
|
*.mdf |
||||
|
*.ldf |
||||
|
|
||||
|
# Business Intelligence projects |
||||
|
*.rdl.data |
||||
|
*.bim.layout |
||||
|
*.bim_*.settings |
||||
|
|
||||
|
# Microsoft Fakes |
||||
|
FakesAssemblies/ |
||||
|
|
||||
|
# GhostDoc plugin setting file |
||||
|
*.GhostDoc.xml |
||||
|
|
||||
|
# Node.js Tools for Visual Studio |
||||
|
.ntvs_analysis.dat |
||||
|
|
||||
|
# Visual Studio 6 build log |
||||
|
*.plg |
||||
|
|
||||
|
# Visual Studio 6 workspace options file |
||||
|
*.opt |
||||
|
|
||||
|
# 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 |
||||
|
|
||||
|
# FAKE - F# Make |
||||
|
.fake/ |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,13 @@ |
|||||
|
# Editor configuration, see http://editorconfig.org |
||||
|
root = true |
||||
|
|
||||
|
[*] |
||||
|
charset = utf-8 |
||||
|
indent_style = space |
||||
|
indent_size = 2 |
||||
|
insert_final_newline = true |
||||
|
trim_trailing_whitespace = true |
||||
|
|
||||
|
[*.md] |
||||
|
max_line_length = off |
||||
|
trim_trailing_whitespace = false |
@ -0,0 +1,40 @@ |
|||||
|
# See http://help.github.com/ignore-files/ for more about ignoring files. |
||||
|
|
||||
|
# compiled output |
||||
|
/dist |
||||
|
/dist-server |
||||
|
/tmp |
||||
|
/out-tsc |
||||
|
|
||||
|
# dependencies |
||||
|
/node_modules |
||||
|
|
||||
|
# IDEs and editors |
||||
|
/.idea |
||||
|
.project |
||||
|
.classpath |
||||
|
.c9/ |
||||
|
*.launch |
||||
|
.settings/ |
||||
|
*.sublime-workspace |
||||
|
|
||||
|
# IDE - VSCode |
||||
|
.vscode/* |
||||
|
!.vscode/settings.json |
||||
|
!.vscode/tasks.json |
||||
|
!.vscode/launch.json |
||||
|
!.vscode/extensions.json |
||||
|
|
||||
|
# misc |
||||
|
/.sass-cache |
||||
|
/connect.lock |
||||
|
/coverage |
||||
|
/libpeerconnection.log |
||||
|
npm-debug.log |
||||
|
yarn-error.log |
||||
|
testem.log |
||||
|
/typings |
||||
|
|
||||
|
# System Files |
||||
|
.DS_Store |
||||
|
Thumbs.db |
@ -0,0 +1,27 @@ |
|||||
|
# DiscoBot |
||||
|
|
||||
|
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0. |
||||
|
|
||||
|
## Development server |
||||
|
|
||||
|
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. |
||||
|
|
||||
|
## Code scaffolding |
||||
|
|
||||
|
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. |
||||
|
|
||||
|
## Build |
||||
|
|
||||
|
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. |
||||
|
|
||||
|
## Running unit tests |
||||
|
|
||||
|
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). |
||||
|
|
||||
|
## Running end-to-end tests |
||||
|
|
||||
|
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). |
||||
|
|
||||
|
## Further help |
||||
|
|
||||
|
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). |
@ -0,0 +1,149 @@ |
|||||
|
{ |
||||
|
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", |
||||
|
"version": 1, |
||||
|
"newProjectRoot": "projects", |
||||
|
"projects": { |
||||
|
"DiscoBot": { |
||||
|
"root": "", |
||||
|
"sourceRoot": "src", |
||||
|
"projectType": "application", |
||||
|
"prefix": "app", |
||||
|
"schematics": {}, |
||||
|
"architect": { |
||||
|
"build": { |
||||
|
"builder": "@angular-devkit/build-angular:browser", |
||||
|
"options": { |
||||
|
"progress": true, |
||||
|
"extractCss": true, |
||||
|
"outputPath": "dist", |
||||
|
"index": "src/index.html", |
||||
|
"main": "src/main.ts", |
||||
|
"polyfills": "src/polyfills.ts", |
||||
|
"tsConfig": "src/tsconfig.app.json", |
||||
|
"assets": [ |
||||
|
"src/assets" |
||||
|
], |
||||
|
"styles": [ |
||||
|
"node_modules/bootstrap/dist/css/bootstrap.min.css", |
||||
|
"src/styles.css" |
||||
|
], |
||||
|
"scripts": [] |
||||
|
}, |
||||
|
"configurations": { |
||||
|
"production": { |
||||
|
"fileReplacements": [ |
||||
|
{ |
||||
|
"replace": "src/environments/environment.ts", |
||||
|
"with": "src/environments/environment.prod.ts" |
||||
|
} |
||||
|
], |
||||
|
"optimization": true, |
||||
|
"outputHashing": "all", |
||||
|
"sourceMap": false, |
||||
|
"extractCss": true, |
||||
|
"namedChunks": false, |
||||
|
"aot": true, |
||||
|
"extractLicenses": true, |
||||
|
"vendorChunk": false, |
||||
|
"buildOptimizer": true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"serve": { |
||||
|
"builder": "@angular-devkit/build-angular:dev-server", |
||||
|
"options": { |
||||
|
"browserTarget": "DiscoBot:build" |
||||
|
}, |
||||
|
"configurations": { |
||||
|
"production": { |
||||
|
"browserTarget": "DiscoBot:build:production" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"extract-i18n": { |
||||
|
"builder": "@angular-devkit/build-angular:extract-i18n", |
||||
|
"options": { |
||||
|
"browserTarget": "DiscoBot:build" |
||||
|
} |
||||
|
}, |
||||
|
"test": { |
||||
|
"builder": "@angular-devkit/build-angular:karma", |
||||
|
"options": { |
||||
|
"main": "src/test.ts", |
||||
|
"polyfills": "src/polyfills.ts", |
||||
|
"tsConfig": "src/tsconfig.spec.json", |
||||
|
"karmaConfig": "src/karma.conf.js", |
||||
|
"styles": [ |
||||
|
"styles.css" |
||||
|
], |
||||
|
"scripts": [], |
||||
|
"assets": [ |
||||
|
"src/assets" |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
"lint": { |
||||
|
"builder": "@angular-devkit/build-angular:tslint", |
||||
|
"options": { |
||||
|
"tsConfig": [ |
||||
|
"src/tsconfig.app.json", |
||||
|
"src/tsconfig.spec.json" |
||||
|
], |
||||
|
"exclude": [ |
||||
|
"**/node_modules/**" |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
"server": { |
||||
|
"builder": "@angular-devkit/build-angular:server", |
||||
|
"options": { |
||||
|
"outputPath": "dist-server", |
||||
|
"main": "src/main.ts", |
||||
|
"tsConfig": "src/tsconfig.server.json" |
||||
|
}, |
||||
|
"configurations": { |
||||
|
"dev": { |
||||
|
"optimization": true, |
||||
|
"outputHashing": "all", |
||||
|
"sourceMap": false, |
||||
|
"namedChunks": false, |
||||
|
"extractLicenses": true, |
||||
|
"vendorChunk": true |
||||
|
}, |
||||
|
"production": { |
||||
|
"optimization": true, |
||||
|
"outputHashing": "all", |
||||
|
"sourceMap": false, |
||||
|
"namedChunks": false, |
||||
|
"extractLicenses": true, |
||||
|
"vendorChunk": false |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"DiscoBot-e2e": { |
||||
|
"root": "e2e/", |
||||
|
"projectType": "application", |
||||
|
"architect": { |
||||
|
"e2e": { |
||||
|
"builder": "@angular-devkit/build-angular:protractor", |
||||
|
"options": { |
||||
|
"protractorConfig": "e2e/protractor.conf.js", |
||||
|
"devServerTarget": "DiscoBot:serve" |
||||
|
} |
||||
|
}, |
||||
|
"lint": { |
||||
|
"builder": "@angular-devkit/build-angular:tslint", |
||||
|
"options": { |
||||
|
"tsConfig": "e2e/tsconfig.e2e.json", |
||||
|
"exclude": [ |
||||
|
"**/node_modules/**" |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"defaultProject": "DiscoBot" |
||||
|
} |
@ -0,0 +1,28 @@ |
|||||
|
// Protractor configuration file, see link for more information
|
||||
|
// https://github.com/angular/protractor/blob/master/lib/config.ts
|
||||
|
|
||||
|
const { SpecReporter } = require('jasmine-spec-reporter'); |
||||
|
|
||||
|
exports.config = { |
||||
|
allScriptsTimeout: 11000, |
||||
|
specs: [ |
||||
|
'./src/**/*.e2e-spec.ts' |
||||
|
], |
||||
|
capabilities: { |
||||
|
'browserName': 'chrome' |
||||
|
}, |
||||
|
directConnect: true, |
||||
|
baseUrl: 'http://localhost:4200/', |
||||
|
framework: 'jasmine', |
||||
|
jasmineNodeOpts: { |
||||
|
showColors: true, |
||||
|
defaultTimeoutInterval: 30000, |
||||
|
print: function() {} |
||||
|
}, |
||||
|
onPrepare() { |
||||
|
require('ts-node').register({ |
||||
|
project: require('path').join(__dirname, './tsconfig.e2e.json') |
||||
|
}); |
||||
|
jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); |
||||
|
} |
||||
|
}; |
@ -0,0 +1,14 @@ |
|||||
|
import { AppPage } from './app.po'; |
||||
|
|
||||
|
describe('App', () => { |
||||
|
let page: AppPage; |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
page = new AppPage(); |
||||
|
}); |
||||
|
|
||||
|
it('should display welcome message', () => { |
||||
|
page.navigateTo(); |
||||
|
expect(page.getMainHeading()).toEqual('Hello, world!'); |
||||
|
}); |
||||
|
}); |
@ -0,0 +1,11 @@ |
|||||
|
import { browser, by, element } from 'protractor'; |
||||
|
|
||||
|
export class AppPage { |
||||
|
navigateTo() { |
||||
|
return browser.get('/'); |
||||
|
} |
||||
|
|
||||
|
getMainHeading() { |
||||
|
return element(by.css('app-root h1')).getText(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,13 @@ |
|||||
|
{ |
||||
|
"extends": "../tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../out-tsc/app", |
||||
|
"module": "commonjs", |
||||
|
"target": "es5", |
||||
|
"types": [ |
||||
|
"jasmine", |
||||
|
"jasminewd2", |
||||
|
"node" |
||||
|
] |
||||
|
} |
||||
|
} |
File diff suppressed because it is too large
@ -0,0 +1,59 @@ |
|||||
|
{ |
||||
|
"name": "DiscoBot", |
||||
|
"version": "0.0.0", |
||||
|
"scripts": { |
||||
|
"ng": "ng", |
||||
|
"start": "ng serve", |
||||
|
"build": "ng build", |
||||
|
"build:ssr": "ng run DiscoBot:server:dev", |
||||
|
"test": "ng test", |
||||
|
"lint": "ng lint", |
||||
|
"e2e": "ng e2e" |
||||
|
}, |
||||
|
"private": true, |
||||
|
"dependencies": { |
||||
|
"@angular/animations": "7.2.5", |
||||
|
"@angular/common": "7.2.5", |
||||
|
"@angular/compiler": "7.2.5", |
||||
|
"@angular/core": "7.2.5", |
||||
|
"@angular/forms": "7.2.5", |
||||
|
"@angular/http": "7.2.5", |
||||
|
"@angular/platform-browser": "7.2.5", |
||||
|
"@angular/platform-browser-dynamic": "7.2.5", |
||||
|
"@angular/platform-server": "7.2.5", |
||||
|
"@angular/router": "7.2.5", |
||||
|
"@nguniversal/module-map-ngfactory-loader": "7.1.0", |
||||
|
"core-js": "^2.6.5", |
||||
|
"rxjs": "^6.4.0", |
||||
|
"zone.js": "^0.8.29", |
||||
|
"aspnet-prerendering": "^3.0.1", |
||||
|
"bootstrap": "^4.3.1", |
||||
|
"jquery": "3.3.1", |
||||
|
"oidc-client": "^1.6.1", |
||||
|
"popper.js": "^1.14.3" |
||||
|
}, |
||||
|
"devDependencies": { |
||||
|
"@angular-devkit/build-angular": "~0.13.2", |
||||
|
"@angular/cli": "~7.3.2", |
||||
|
"@angular/compiler-cli": "7.2.5", |
||||
|
"@angular/language-service": "^7.2.5", |
||||
|
"@types/jasmine": "~3.3.9", |
||||
|
"@types/jasminewd2": "~2.0.6", |
||||
|
"@types/node": "~11.9.4", |
||||
|
"codelyzer": "~4.5.0", |
||||
|
"jasmine-core": "~3.3.0", |
||||
|
"jasmine-spec-reporter": "~4.2.1", |
||||
|
"karma": "^4.0.0", |
||||
|
"karma-chrome-launcher": "~2.2.0", |
||||
|
"karma-coverage-istanbul-reporter": "~2.0.5", |
||||
|
"karma-jasmine": "~2.0.1", |
||||
|
"karma-jasmine-html-reporter": "^1.4.0", |
||||
|
"typescript": "~3.2.4" |
||||
|
}, |
||||
|
"optionalDependencies": { |
||||
|
"node-sass": "^4.9.3", |
||||
|
"protractor": "~5.4.0", |
||||
|
"ts-node": "~5.0.1", |
||||
|
"tslint": "~5.9.1" |
||||
|
} |
||||
|
} |
@ -0,0 +1,6 @@ |
|||||
|
<body> |
||||
|
<app-nav-menu></app-nav-menu> |
||||
|
<div class="container"> |
||||
|
<router-outlet></router-outlet> |
||||
|
</div> |
||||
|
</body> |
@ -0,0 +1,9 @@ |
|||||
|
import { Component } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-root', |
||||
|
templateUrl: './app.component.html' |
||||
|
}) |
||||
|
export class AppComponent { |
||||
|
title = 'app'; |
||||
|
} |
@ -0,0 +1,34 @@ |
|||||
|
import { BrowserModule } from '@angular/platform-browser'; |
||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { FormsModule } from '@angular/forms'; |
||||
|
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; |
||||
|
import { RouterModule } from '@angular/router'; |
||||
|
|
||||
|
import { AppComponent } from './app.component'; |
||||
|
import { NavMenuComponent } from './nav-menu/nav-menu.component'; |
||||
|
import { HomeComponent } from './home/home.component'; |
||||
|
import { CounterComponent } from './counter/counter.component'; |
||||
|
import { FetchDataComponent } from './fetch-data/fetch-data.component'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
declarations: [ |
||||
|
AppComponent, |
||||
|
NavMenuComponent, |
||||
|
HomeComponent, |
||||
|
CounterComponent, |
||||
|
FetchDataComponent |
||||
|
], |
||||
|
imports: [ |
||||
|
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }), |
||||
|
HttpClientModule, |
||||
|
FormsModule, |
||||
|
RouterModule.forRoot([ |
||||
|
{ path: '', component: HomeComponent, pathMatch: 'full' }, |
||||
|
{ path: 'counter', component: CounterComponent }, |
||||
|
{ path: 'fetch-data', component: FetchDataComponent }, |
||||
|
]) |
||||
|
], |
||||
|
providers: [], |
||||
|
bootstrap: [AppComponent] |
||||
|
}) |
||||
|
export class AppModule { } |
@ -0,0 +1,11 @@ |
|||||
|
import { NgModule } from '@angular/core'; |
||||
|
import { ServerModule } from '@angular/platform-server'; |
||||
|
import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader'; |
||||
|
import { AppComponent } from './app.component'; |
||||
|
import { AppModule } from './app.module'; |
||||
|
|
||||
|
@NgModule({ |
||||
|
imports: [AppModule, ServerModule, ModuleMapLoaderModule], |
||||
|
bootstrap: [AppComponent] |
||||
|
}) |
||||
|
export class AppServerModule { } |
@ -0,0 +1,7 @@ |
|||||
|
<h1>Counter</h1> |
||||
|
|
||||
|
<p>This is a simple example of an Angular component.</p> |
||||
|
|
||||
|
<p>Current count: <strong>{{ currentCount }}</strong></p> |
||||
|
|
||||
|
<button class="btn btn-primary" (click)="incrementCounter()">Increment</button> |
@ -0,0 +1,36 @@ |
|||||
|
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; |
||||
|
|
||||
|
import { CounterComponent } from './counter.component'; |
||||
|
|
||||
|
describe('CounterComponent', () => { |
||||
|
let component: CounterComponent; |
||||
|
let fixture: ComponentFixture<CounterComponent>; |
||||
|
|
||||
|
beforeEach(async(() => { |
||||
|
TestBed.configureTestingModule({ |
||||
|
declarations: [ CounterComponent ] |
||||
|
}) |
||||
|
.compileComponents(); |
||||
|
})); |
||||
|
|
||||
|
beforeEach(() => { |
||||
|
fixture = TestBed.createComponent(CounterComponent); |
||||
|
component = fixture.componentInstance; |
||||
|
fixture.detectChanges(); |
||||
|
}); |
||||
|
|
||||
|
it('should display a title', async(() => { |
||||
|
const titleText = fixture.nativeElement.querySelector('h1').textContent; |
||||
|
expect(titleText).toEqual('Counter'); |
||||
|
})); |
||||
|
|
||||
|
it('should start with count 0, then increments by 1 when clicked', async(() => { |
||||
|
const countElement = fixture.nativeElement.querySelector('strong'); |
||||
|
expect(countElement.textContent).toEqual('0'); |
||||
|
|
||||
|
const incrementButton = fixture.nativeElement.querySelector('button'); |
||||
|
incrementButton.click(); |
||||
|
fixture.detectChanges(); |
||||
|
expect(countElement.textContent).toEqual('1'); |
||||
|
})); |
||||
|
}); |
@ -0,0 +1,13 @@ |
|||||
|
import { Component } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-counter-component', |
||||
|
templateUrl: './counter.component.html' |
||||
|
}) |
||||
|
export class CounterComponent { |
||||
|
public currentCount = 0; |
||||
|
|
||||
|
public incrementCounter() { |
||||
|
this.currentCount = this.currentCount+4; |
||||
|
} |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
<h1>Weather forecast</h1> |
||||
|
|
||||
|
<p>This component demonstrates fetching data from the server.</p> |
||||
|
|
||||
|
<p *ngIf="!forecasts"><em>Loading...</em></p> |
||||
|
|
||||
|
<table class='table table-striped' *ngIf="forecasts"> |
||||
|
<thead> |
||||
|
<tr> |
||||
|
<th>Date</th> |
||||
|
<th>Temp. (C)</th> |
||||
|
<th>Temp. (F)</th> |
||||
|
<th>Summary</th> |
||||
|
</tr> |
||||
|
</thead> |
||||
|
<tbody> |
||||
|
<tr *ngFor="let forecast of forecasts"> |
||||
|
<td>{{ forecast.dateFormatted }}</td> |
||||
|
<td>{{ forecast.temperatureC }}</td> |
||||
|
<td>{{ forecast.temperatureF }}</td> |
||||
|
<td>{{ forecast.summary }}</td> |
||||
|
</tr> |
||||
|
</tbody> |
||||
|
</table> |
@ -0,0 +1,23 @@ |
|||||
|
import { Component, Inject } from '@angular/core'; |
||||
|
import { HttpClient } from '@angular/common/http'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-fetch-data', |
||||
|
templateUrl: './fetch-data.component.html' |
||||
|
}) |
||||
|
export class FetchDataComponent { |
||||
|
public forecasts: WeatherForecast[]; |
||||
|
|
||||
|
constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) { |
||||
|
http.get<WeatherForecast[]>(baseUrl + 'api/SampleData/WeatherForecasts').subscribe(result => { |
||||
|
this.forecasts = result; |
||||
|
}, error => console.error(error)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
interface WeatherForecast { |
||||
|
dateFormatted: string; |
||||
|
temperatureC: number; |
||||
|
temperatureF: number; |
||||
|
summary: string; |
||||
|
} |
@ -0,0 +1,14 @@ |
|||||
|
<h1>Hello, world!</h1> |
||||
|
<p>Welcome to your new single-page application, built with:</p> |
||||
|
<ul> |
||||
|
<li><a href='https://get.asp.net/'>ASP.NET Core</a> and <a href='https://msdn.microsoft.com/en-us/library/67ef8sbd.aspx'>C#</a> for cross-platform server-side code</li> |
||||
|
<li><a href='https://angular.io/'>Angular</a> and <a href='http://www.typescriptlang.org/'>TypeScript</a> for client-side code</li> |
||||
|
<li><a href='http://getbootstrap.com/'>Bootstrap</a> for layout and styling</li> |
||||
|
</ul> |
||||
|
<p>To help you get started, we've also set up:</p> |
||||
|
<ul> |
||||
|
<li><strong>Client-side navigation</strong>. For example, click <em>Counter</em> then <em>Back</em> to return here.</li> |
||||
|
<li><strong>Angular CLI integration</strong>. In development mode, there's no need to run <code>ng serve</code>. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.</li> |
||||
|
<li><strong>Efficient production builds</strong>. In production mode, development-time features are disabled, and your <code>dotnet publish</code> configuration automatically invokes <code>ng build</code> to produce minified, ahead-of-time compiled JavaScript files.</li> |
||||
|
</ul> |
||||
|
<p>The <code>ClientApp</code> subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any <code>ng</code> command (e.g., <code>ng test</code>), or use <code>npm</code> to install extra packages into it.</p> |
@ -0,0 +1,8 @@ |
|||||
|
import { Component } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-home', |
||||
|
templateUrl: './home.component.html', |
||||
|
}) |
||||
|
export class HomeComponent { |
||||
|
} |
@ -0,0 +1,18 @@ |
|||||
|
a.navbar-brand { |
||||
|
white-space: normal; |
||||
|
text-align: center; |
||||
|
word-break: break-all; |
||||
|
} |
||||
|
|
||||
|
html { |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
@media (min-width: 768px) { |
||||
|
html { |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.box-shadow { |
||||
|
box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); |
||||
|
} |
@ -0,0 +1,24 @@ |
|||||
|
<header> |
||||
|
<nav class='navbar navbar-expand-sm navbar-toggleable-sm navbar-light bg-white border-bottom box-shadow mb-3'> |
||||
|
<div class="container"> |
||||
|
<a class="navbar-brand" [routerLink]='["/"]'>DiscoBot</a> |
||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target=".navbar-collapse" aria-label="Toggle navigation" |
||||
|
[attr.aria-expanded]="isExpanded" (click)="toggle()"> |
||||
|
<span class="navbar-toggler-icon"></span> |
||||
|
</button> |
||||
|
<div class="navbar-collapse collapse d-sm-inline-flex flex-sm-row-reverse" [ngClass]='{"show": isExpanded}'> |
||||
|
<ul class="navbar-nav flex-grow"> |
||||
|
<li class="nav-item" [routerLinkActive]='["link-active"]' [routerLinkActiveOptions]='{ exact: true }'> |
||||
|
<a class="nav-link text-dark" [routerLink]='["/"]'>Home</a> |
||||
|
</li> |
||||
|
<li class="nav-item" [routerLinkActive]='["link-active"]'> |
||||
|
<a class="nav-link text-dark" [routerLink]='["/counter"]'>Counter</a> |
||||
|
</li> |
||||
|
<li class="nav-item" [routerLinkActive]='["link-active"]'> |
||||
|
<a class="nav-link text-dark" [routerLink]='["/fetch-data"]'>Fetch data</a> |
||||
|
</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
</div> |
||||
|
</nav> |
||||
|
</header> |
@ -0,0 +1,18 @@ |
|||||
|
import { Component } from '@angular/core'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'app-nav-menu', |
||||
|
templateUrl: './nav-menu.component.html', |
||||
|
styleUrls: ['./nav-menu.component.css'] |
||||
|
}) |
||||
|
export class NavMenuComponent { |
||||
|
isExpanded = false; |
||||
|
|
||||
|
collapse() { |
||||
|
this.isExpanded = false; |
||||
|
} |
||||
|
|
||||
|
toggle() { |
||||
|
this.isExpanded = !this.isExpanded; |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers |
||||
|
# For additional information regarding the format and rule options, please see: |
||||
|
# https://github.com/browserslist/browserslist#queries |
||||
|
# For IE 9-11 support, please uncomment the last line of the file and adjust as needed |
||||
|
> 0.5% |
||||
|
last 2 versions |
||||
|
Firefox ESR |
||||
|
not dead |
||||
|
# IE 9-11 |
@ -0,0 +1,3 @@ |
|||||
|
export const environment = { |
||||
|
production: true |
||||
|
}; |
@ -0,0 +1,15 @@ |
|||||
|
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
|
// `ng build ---prod` replaces `environment.ts` with `environment.prod.ts`.
|
||||
|
// The list of file replacements can be found in `angular.json`.
|
||||
|
|
||||
|
export const environment = { |
||||
|
production: false |
||||
|
}; |
||||
|
|
||||
|
/* |
||||
|
* In development mode, to ignore zone related error stack frames such as |
||||
|
* `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can |
||||
|
* import the following file, but please comment it out in production mode |
||||
|
* because it will have performance impact when throw error |
||||
|
*/ |
||||
|
// import 'zone.js/dist/zone-error'; // Included with Angular CLI.
|
@ -0,0 +1,14 @@ |
|||||
|
<!doctype html> |
||||
|
<html lang="en"> |
||||
|
<head> |
||||
|
<meta charset="utf-8"> |
||||
|
<title>DiscoBot</title> |
||||
|
<base href="/"> |
||||
|
|
||||
|
<meta name="viewport" content="width=device-width, initial-scale=1"> |
||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico"> |
||||
|
</head> |
||||
|
<body> |
||||
|
<app-root>Loading...</app-root> |
||||
|
</body> |
||||
|
</html> |
@ -0,0 +1,31 @@ |
|||||
|
// Karma configuration file, see link for more information
|
||||
|
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
|
||||
|
module.exports = function (config) { |
||||
|
config.set({ |
||||
|
basePath: '', |
||||
|
frameworks: ['jasmine', '@angular-devkit/build-angular'], |
||||
|
plugins: [ |
||||
|
require('karma-jasmine'), |
||||
|
require('karma-chrome-launcher'), |
||||
|
require('karma-jasmine-html-reporter'), |
||||
|
require('karma-coverage-istanbul-reporter'), |
||||
|
require('@angular-devkit/build-angular/plugins/karma') |
||||
|
], |
||||
|
client: { |
||||
|
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
|
}, |
||||
|
coverageIstanbulReporter: { |
||||
|
dir: require('path').join(__dirname, '../coverage'), |
||||
|
reports: ['html', 'lcovonly'], |
||||
|
fixWebpackSourcePaths: true |
||||
|
}, |
||||
|
reporters: ['progress', 'kjhtml'], |
||||
|
port: 9876, |
||||
|
colors: true, |
||||
|
logLevel: config.LOG_INFO, |
||||
|
autoWatch: true, |
||||
|
browsers: ['Chrome'], |
||||
|
singleRun: false |
||||
|
}); |
||||
|
}; |
@ -0,0 +1,20 @@ |
|||||
|
import { enableProdMode } from '@angular/core'; |
||||
|
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; |
||||
|
|
||||
|
import { AppModule } from './app/app.module'; |
||||
|
import { environment } from './environments/environment'; |
||||
|
|
||||
|
export function getBaseUrl() { |
||||
|
return document.getElementsByTagName('base')[0].href; |
||||
|
} |
||||
|
|
||||
|
const providers = [ |
||||
|
{ provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] } |
||||
|
]; |
||||
|
|
||||
|
if (environment.production) { |
||||
|
enableProdMode(); |
||||
|
} |
||||
|
|
||||
|
platformBrowserDynamic(providers).bootstrapModule(AppModule) |
||||
|
.catch(err => console.log(err)); |
@ -0,0 +1,80 @@ |
|||||
|
/** |
||||
|
* This file includes polyfills needed by Angular and is loaded before the app. |
||||
|
* You can add your own extra polyfills to this file. |
||||
|
* |
||||
|
* This file is divided into 2 sections: |
||||
|
* 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. |
||||
|
* 2. Application imports. Files imported after ZoneJS that should be loaded before your main |
||||
|
* file. |
||||
|
* |
||||
|
* The current setup is for so-called "evergreen" browsers; the last versions of browsers that |
||||
|
* automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), |
||||
|
* Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. |
||||
|
* |
||||
|
* Learn more in https://angular.io/docs/ts/latest/guide/browser-support.html
|
||||
|
*/ |
||||
|
|
||||
|
/*************************************************************************************************** |
||||
|
* BROWSER POLYFILLS |
||||
|
*/ |
||||
|
|
||||
|
/** IE9, IE10 and IE11 requires all of the following polyfills. **/ |
||||
|
// import 'core-js/es6/symbol';
|
||||
|
// import 'core-js/es6/object';
|
||||
|
// import 'core-js/es6/function';
|
||||
|
// import 'core-js/es6/parse-int';
|
||||
|
// import 'core-js/es6/parse-float';
|
||||
|
// import 'core-js/es6/number';
|
||||
|
// import 'core-js/es6/math';
|
||||
|
// import 'core-js/es6/string';
|
||||
|
// import 'core-js/es6/date';
|
||||
|
// import 'core-js/es6/array';
|
||||
|
// import 'core-js/es6/regexp';
|
||||
|
// import 'core-js/es6/map';
|
||||
|
// import 'core-js/es6/weak-map';
|
||||
|
// import 'core-js/es6/set';
|
||||
|
|
||||
|
/** IE10 and IE11 requires the following for NgClass support on SVG elements */ |
||||
|
// import 'classlist.js'; // Run `npm install --save classlist.js`.
|
||||
|
|
||||
|
/** IE10 and IE11 requires the following for the Reflect API. */ |
||||
|
// import 'core-js/es6/reflect';
|
||||
|
|
||||
|
|
||||
|
/** Evergreen browsers require these. **/ |
||||
|
// Used for reflect-metadata in JIT. If you use AOT (and only Angular decorators), you can remove.
|
||||
|
import 'core-js/es7/reflect'; |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* Web Animations `@angular/platform-browser/animations` |
||||
|
* Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. |
||||
|
* Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). |
||||
|
**/ |
||||
|
// import 'web-animations-js'; // Run `npm install --save web-animations-js`.
|
||||
|
|
||||
|
/** |
||||
|
* By default, zone.js will patch all possible macroTask and DomEvents |
||||
|
* user can disable parts of macroTask/DomEvents patch by setting following flags |
||||
|
*/ |
||||
|
|
||||
|
// (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
|
||||
|
// (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
|
||||
|
// (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
|
||||
|
|
||||
|
/* |
||||
|
* in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js |
||||
|
* with the following flag, it will bypass `zone.js` patch for IE/Edge |
||||
|
*/ |
||||
|
// (window as any).__Zone_enable_cross_context_check = true;
|
||||
|
|
||||
|
/*************************************************************************************************** |
||||
|
* Zone JS is required by default for Angular itself. |
||||
|
*/ |
||||
|
import 'zone.js/dist/zone'; // Included with Angular CLI.
|
||||
|
|
||||
|
|
||||
|
|
||||
|
/*************************************************************************************************** |
||||
|
* APPLICATION IMPORTS |
||||
|
*/ |
@ -0,0 +1 @@ |
|||||
|
/* You can add global styles to this file, and also import other style files */ |
@ -0,0 +1,20 @@ |
|||||
|
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
|
||||
|
|
||||
|
import 'zone.js/dist/zone-testing'; |
||||
|
import { getTestBed } from '@angular/core/testing'; |
||||
|
import { |
||||
|
BrowserDynamicTestingModule, |
||||
|
platformBrowserDynamicTesting |
||||
|
} from '@angular/platform-browser-dynamic/testing'; |
||||
|
|
||||
|
declare const require: any; |
||||
|
|
||||
|
// First, initialize the Angular testing environment.
|
||||
|
getTestBed().initTestEnvironment( |
||||
|
BrowserDynamicTestingModule, |
||||
|
platformBrowserDynamicTesting() |
||||
|
); |
||||
|
// Then we find all the tests.
|
||||
|
const context = require.context('./', true, /\.spec\.ts$/); |
||||
|
// And load the modules.
|
||||
|
context.keys().map(context); |
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"extends": "../tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../out-tsc/app", |
||||
|
"module": "es2015", |
||||
|
"types": [] |
||||
|
}, |
||||
|
"exclude": [ |
||||
|
"src/test.ts", |
||||
|
"**/*.spec.ts" |
||||
|
] |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"extends": "../tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"module": "commonjs" |
||||
|
}, |
||||
|
"angularCompilerOptions": { |
||||
|
"entryModule": "app/app.server.module#AppServerModule" |
||||
|
} |
||||
|
} |
@ -0,0 +1,19 @@ |
|||||
|
{ |
||||
|
"extends": "../tsconfig.json", |
||||
|
"compilerOptions": { |
||||
|
"outDir": "../out-tsc/spec", |
||||
|
"module": "commonjs", |
||||
|
"types": [ |
||||
|
"jasmine", |
||||
|
"node" |
||||
|
] |
||||
|
}, |
||||
|
"files": [ |
||||
|
"test.ts", |
||||
|
"polyfills.ts" |
||||
|
], |
||||
|
"include": [ |
||||
|
"**/*.spec.ts", |
||||
|
"**/*.d.ts" |
||||
|
] |
||||
|
} |
@ -0,0 +1,17 @@ |
|||||
|
{ |
||||
|
"extends": "../tslint.json", |
||||
|
"rules": { |
||||
|
"directive-selector": [ |
||||
|
true, |
||||
|
"attribute", |
||||
|
"app", |
||||
|
"camelCase" |
||||
|
], |
||||
|
"component-selector": [ |
||||
|
true, |
||||
|
"element", |
||||
|
"app", |
||||
|
"kebab-case" |
||||
|
] |
||||
|
} |
||||
|
} |
@ -0,0 +1,20 @@ |
|||||
|
{ |
||||
|
"compileOnSave": false, |
||||
|
"compilerOptions": { |
||||
|
"baseUrl": "./", |
||||
|
"outDir": "./dist/out-tsc", |
||||
|
"sourceMap": true, |
||||
|
"declaration": false, |
||||
|
"moduleResolution": "node", |
||||
|
"emitDecoratorMetadata": true, |
||||
|
"experimentalDecorators": true, |
||||
|
"target": "es5", |
||||
|
"typeRoots": [ |
||||
|
"node_modules/@types" |
||||
|
], |
||||
|
"lib": [ |
||||
|
"es2017", |
||||
|
"dom" |
||||
|
] |
||||
|
} |
||||
|
} |
@ -0,0 +1,130 @@ |
|||||
|
{ |
||||
|
"rulesDirectory": [ |
||||
|
"node_modules/codelyzer" |
||||
|
], |
||||
|
"rules": { |
||||
|
"arrow-return-shorthand": true, |
||||
|
"callable-types": true, |
||||
|
"class-name": true, |
||||
|
"comment-format": [ |
||||
|
true, |
||||
|
"check-space" |
||||
|
], |
||||
|
"curly": true, |
||||
|
"deprecation": { |
||||
|
"severity": "warn" |
||||
|
}, |
||||
|
"eofline": true, |
||||
|
"forin": true, |
||||
|
"import-blacklist": [ |
||||
|
true, |
||||
|
"rxjs/Rx" |
||||
|
], |
||||
|
"import-spacing": true, |
||||
|
"indent": [ |
||||
|
true, |
||||
|
"spaces" |
||||
|
], |
||||
|
"interface-over-type-literal": true, |
||||
|
"label-position": true, |
||||
|
"max-line-length": [ |
||||
|
true, |
||||
|
140 |
||||
|
], |
||||
|
"member-access": false, |
||||
|
"member-ordering": [ |
||||
|
true, |
||||
|
{ |
||||
|
"order": [ |
||||
|
"static-field", |
||||
|
"instance-field", |
||||
|
"static-method", |
||||
|
"instance-method" |
||||
|
] |
||||
|
} |
||||
|
], |
||||
|
"no-arg": true, |
||||
|
"no-bitwise": true, |
||||
|
"no-console": [ |
||||
|
true, |
||||
|
"debug", |
||||
|
"info", |
||||
|
"time", |
||||
|
"timeEnd", |
||||
|
"trace" |
||||
|
], |
||||
|
"no-construct": true, |
||||
|
"no-debugger": true, |
||||
|
"no-duplicate-super": true, |
||||
|
"no-empty": false, |
||||
|
"no-empty-interface": true, |
||||
|
"no-eval": true, |
||||
|
"no-inferrable-types": [ |
||||
|
true, |
||||
|
"ignore-params" |
||||
|
], |
||||
|
"no-misused-new": true, |
||||
|
"no-non-null-assertion": true, |
||||
|
"no-shadowed-variable": true, |
||||
|
"no-string-literal": false, |
||||
|
"no-string-throw": true, |
||||
|
"no-switch-case-fall-through": true, |
||||
|
"no-trailing-whitespace": true, |
||||
|
"no-unnecessary-initializer": true, |
||||
|
"no-unused-expression": true, |
||||
|
"no-use-before-declare": true, |
||||
|
"no-var-keyword": true, |
||||
|
"object-literal-sort-keys": false, |
||||
|
"one-line": [ |
||||
|
true, |
||||
|
"check-open-brace", |
||||
|
"check-catch", |
||||
|
"check-else", |
||||
|
"check-whitespace" |
||||
|
], |
||||
|
"prefer-const": true, |
||||
|
"quotemark": [ |
||||
|
true, |
||||
|
"single" |
||||
|
], |
||||
|
"radix": true, |
||||
|
"semicolon": [ |
||||
|
true, |
||||
|
"always" |
||||
|
], |
||||
|
"triple-equals": [ |
||||
|
true, |
||||
|
"allow-null-check" |
||||
|
], |
||||
|
"typedef-whitespace": [ |
||||
|
true, |
||||
|
{ |
||||
|
"call-signature": "nospace", |
||||
|
"index-signature": "nospace", |
||||
|
"parameter": "nospace", |
||||
|
"property-declaration": "nospace", |
||||
|
"variable-declaration": "nospace" |
||||
|
} |
||||
|
], |
||||
|
"unified-signatures": true, |
||||
|
"variable-name": false, |
||||
|
"whitespace": [ |
||||
|
true, |
||||
|
"check-branch", |
||||
|
"check-decl", |
||||
|
"check-operator", |
||||
|
"check-separator", |
||||
|
"check-type" |
||||
|
], |
||||
|
"no-output-on-prefix": true, |
||||
|
"use-input-property-decorator": true, |
||||
|
"use-output-property-decorator": true, |
||||
|
"use-host-property-decorator": true, |
||||
|
"no-input-rename": true, |
||||
|
"no-output-rename": true, |
||||
|
"use-life-cycle-interface": true, |
||||
|
"use-pipe-transform-interface": true, |
||||
|
"component-class-suffix": true, |
||||
|
"directive-class-suffix": true |
||||
|
} |
||||
|
} |
@ -0,0 +1,44 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
|
||||
|
namespace DiscoBot.Controllers |
||||
|
{ |
||||
|
[Route("api/[controller]")]
|
||||
|
public class SampleDataController : Controller |
||||
|
{ |
||||
|
private static string[] Summaries = new[] |
||||
|
{ |
||||
|
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" |
||||
|
}; |
||||
|
|
||||
|
[HttpGet("[action]")]
|
||||
|
public IEnumerable<WeatherForecast> WeatherForecasts() |
||||
|
{ |
||||
|
var rng = new Random(); |
||||
|
return Enumerable.Range(1, 5).Select(index => new WeatherForecast |
||||
|
{ |
||||
|
DateFormatted = DateTime.Now.AddDays(index).ToString("d"), |
||||
|
TemperatureC = rng.Next(-20, 55), |
||||
|
Summary = Summaries[rng.Next(Summaries.Length)] |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
public class WeatherForecast |
||||
|
{ |
||||
|
public string DateFormatted { get; set; } |
||||
|
public int TemperatureC { get; set; } |
||||
|
public string Summary { get; set; } |
||||
|
|
||||
|
public int TemperatureF |
||||
|
{ |
||||
|
get |
||||
|
{ |
||||
|
return 32 + (int)(TemperatureC / 0.5556); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,174 @@ |
|||||
|
using Discord; |
||||
|
using Discord.WebSocket; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Reflection; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace DiscoBot |
||||
|
{ |
||||
|
public class DisBot |
||||
|
{ |
||||
|
private DiscordSocketClient discordClient; |
||||
|
private WSController wsC; |
||||
|
private List<Type> moduleTypes = new List<Type>(); |
||||
|
private Dictionary<ulong, Dictionary<string, Func<SocketMessage, string[], Task>>> guildcommands = new Dictionary<ulong, Dictionary<string, Func<SocketMessage, string[], Task>>>(); |
||||
|
public DisBot(WSController wsC) |
||||
|
{ |
||||
|
this.wsC = wsC; |
||||
|
} |
||||
|
|
||||
|
public async void Initialize() |
||||
|
{ |
||||
|
Console.WriteLine("In init!"); |
||||
|
discordClient = new DiscordSocketClient(); |
||||
|
|
||||
|
await discordClient.LoginAsync(TokenType.Bot, "NTUxNDcxODcxNDcyNjk3MzQ1.D3DVtw.Weh-a3l2XsBGuD5N1-rLKfnZ8wI"); |
||||
|
await discordClient.StartAsync(); |
||||
|
|
||||
|
discordClient.Log += Log; |
||||
|
discordClient.JoinedGuild += JoinedGuild; |
||||
|
discordClient.GuildAvailable += GuildAvailable; |
||||
|
discordClient.Connected += Connected; |
||||
|
discordClient.Ready += Ready; |
||||
|
discordClient.LeftGuild += LeftGuild; |
||||
|
discordClient.MessageReceived += MessageReceived; |
||||
|
|
||||
|
Console.WriteLine("Looking for modules..."); |
||||
|
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); |
||||
|
foreach(Assembly a in assemblies) |
||||
|
{ |
||||
|
moduleTypes.AddRange(GetTypesWithInterface(a)); |
||||
|
} |
||||
|
Console.WriteLine("Found " + moduleTypes.Count + " module types."); |
||||
|
} |
||||
|
|
||||
|
private Task Log(LogMessage msg) |
||||
|
{ |
||||
|
Console.WriteLine(msg.ToString()); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task Connected() |
||||
|
{ |
||||
|
Console.WriteLine("CONNECTED WOHOOOO"); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
private Task Ready() |
||||
|
{ |
||||
|
Console.WriteLine("Ready, connected to the following guilds: "); |
||||
|
foreach(SocketGuild guild in discordClient.Guilds) |
||||
|
{ |
||||
|
Console.WriteLine("- "+guild.Name+" | "+guild.Id); |
||||
|
} |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task JoinedGuild(SocketGuild guild) |
||||
|
{ |
||||
|
Console.WriteLine("Joined Guild "+guild.Name); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private void LoadModulesForGuild(SocketGuild guild) |
||||
|
{ |
||||
|
Dictionary<string, Func<SocketMessage, string[], Task>> commands = new Dictionary<string, Func<SocketMessage, string[], Task>>(); |
||||
|
foreach (Type t in moduleTypes) |
||||
|
{ |
||||
|
if (t.IsInterface) |
||||
|
continue; |
||||
|
try |
||||
|
{ |
||||
|
IModule module = (IModule)Activator.CreateInstance(t, new object[] { guild }); |
||||
|
module.Initialize(wsC); |
||||
|
var c = module.Commands; |
||||
|
List<string> keyList = new List<string>(c.Keys); |
||||
|
foreach (string k in keyList) |
||||
|
{ |
||||
|
Console.WriteLine("Found Command: " + k); |
||||
|
} |
||||
|
foreach (var entry in c) |
||||
|
{ |
||||
|
commands.Add(entry.Key, entry.Value); |
||||
|
} |
||||
|
} catch(MissingMethodException) |
||||
|
{ |
||||
|
Console.WriteLine("ERROR: Could not instantiate " + t.FullName + ". Could not find SocketGuild constructor."); |
||||
|
} |
||||
|
} |
||||
|
guildcommands.Add(guild.Id, commands); |
||||
|
} |
||||
|
|
||||
|
private Task GuildAvailable(SocketGuild guild) |
||||
|
{ |
||||
|
Console.WriteLine("Guild available " + guild.Name + " | " + guild.Id); |
||||
|
if (guildcommands.ContainsKey(guild.Id)) |
||||
|
{ |
||||
|
Console.WriteLine("Guild " + guild.Id + " is already loaded."); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
LoadModulesForGuild(guild); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task LeftGuild(SocketGuild guild) |
||||
|
{ |
||||
|
Console.WriteLine("Left Guild " + guild.Name); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task MessageReceived(SocketMessage message) |
||||
|
{ |
||||
|
// Don't parse our own messages
|
||||
|
if (message.Author.Id == discordClient.CurrentUser.Id) |
||||
|
return Task.CompletedTask; |
||||
|
|
||||
|
if(message.Content.StartsWith("!")) |
||||
|
{ |
||||
|
if(message.Channel is IGuildChannel) |
||||
|
{ |
||||
|
var gchan = message.Channel as IGuildChannel; |
||||
|
Console.WriteLine("|"+gchan.Name+"| <" + message.Author + "> " + message.Content); |
||||
|
Dictionary<string, Func<SocketMessage, string[], Task>> commands = guildcommands[gchan.GuildId]; |
||||
|
string cnt = message.Content.Substring(1); |
||||
|
string[] splits = cnt.Split(" "); |
||||
|
string command = splits[0]; |
||||
|
|
||||
|
return Task.Run(() => { |
||||
|
if(commands.ContainsKey(command)) |
||||
|
commands[command](message, splits); |
||||
|
else |
||||
|
message.Channel.SendMessageAsync("Command not found!"); |
||||
|
}); |
||||
|
} else |
||||
|
{ |
||||
|
Console.WriteLine("Out of guild command! "+message.Content); |
||||
|
message.Channel.SendMessageAsync("Commands in DM are not supported. :("); |
||||
|
} |
||||
|
} |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private IEnumerable<Type> GetTypesWithInterface(Assembly asm) |
||||
|
{ |
||||
|
var it = typeof(IModule); |
||||
|
return asm.GetLoadableTypes().Where(it.IsAssignableFrom).ToList(); |
||||
|
} |
||||
|
} |
||||
|
public static class TypeLoaderExtensions |
||||
|
{ |
||||
|
public static IEnumerable<Type> GetLoadableTypes(this Assembly assembly) |
||||
|
{ |
||||
|
if (assembly == null) throw new ArgumentNullException("assembly"); |
||||
|
try |
||||
|
{ |
||||
|
return assembly.GetTypes(); |
||||
|
} |
||||
|
catch (ReflectionTypeLoadException e) |
||||
|
{ |
||||
|
return e.Types.Where(t => t != null); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,58 @@ |
|||||
|
<Project Sdk="Microsoft.NET.Sdk.Web"> |
||||
|
|
||||
|
<PropertyGroup> |
||||
|
<TargetFramework>netcoreapp3.0</TargetFramework> |
||||
|
<TypeScriptCompileBlocked>true</TypeScriptCompileBlocked> |
||||
|
<TypeScriptToolsVersion>Latest</TypeScriptToolsVersion> |
||||
|
<IsPackable>false</IsPackable> |
||||
|
<SpaRoot>ClientApp\</SpaRoot> |
||||
|
<DefaultItemExcludes>$(DefaultItemExcludes);$(SpaRoot)node_modules\**</DefaultItemExcludes> |
||||
|
|
||||
|
<!-- Set this to true if you enable server-side prerendering --> |
||||
|
<BuildServerSideRenderer>false</BuildServerSideRenderer> |
||||
|
</PropertyGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<PackageReference Include="Discord.Net" Version="2.0.1" /> |
||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.0.0-preview3-19153-02" /> |
||||
|
<PackageReference Include="Microsoft.AspNetCore.SpaServices.Extensions" Version="3.0.0-preview3-19153-02" /> |
||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0-preview4.19216.3" /> |
||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0-preview4.19216.3" /> |
||||
|
<PackageReference Include="System.ServiceModel.Syndication" Version="4.6.0-preview4.19212.13" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<ItemGroup> |
||||
|
<!-- Don't publish the SPA source files, but do show them in the project files list --> |
||||
|
<Content Remove="$(SpaRoot)**" /> |
||||
|
<None Remove="$(SpaRoot)**" /> |
||||
|
<None Include="$(SpaRoot)**" Exclude="$(SpaRoot)node_modules\**" /> |
||||
|
</ItemGroup> |
||||
|
|
||||
|
<Target Name="DebugEnsureNodeEnv" BeforeTargets="Build" Condition=" '$(Configuration)' == 'Debug' And !Exists('$(SpaRoot)node_modules') "> |
||||
|
<!-- Ensure Node.js is installed --> |
||||
|
<Exec Command="node --version" ContinueOnError="true"> |
||||
|
<Output TaskParameter="ExitCode" PropertyName="ErrorCode" /> |
||||
|
</Exec> |
||||
|
<Error Condition="'$(ErrorCode)' != '0'" Text="Node.js is required to build and run this project. To continue, please install Node.js from https://nodejs.org/, and then restart your command prompt or IDE." /> |
||||
|
<Message Importance="high" Text="Restoring dependencies using 'npm'. This may take several minutes..." /> |
||||
|
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" /> |
||||
|
</Target> |
||||
|
|
||||
|
<Target Name="PublishRunWebpack" AfterTargets="ComputeFilesToPublish"> |
||||
|
<!-- As part of publishing, ensure the JS resources are freshly built in production mode --> |
||||
|
<Exec WorkingDirectory="$(SpaRoot)" Command="npm install" /> |
||||
|
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build -- --prod" /> |
||||
|
<Exec WorkingDirectory="$(SpaRoot)" Command="npm run build:ssr -- --prod" Condition=" '$(BuildServerSideRenderer)' == 'true' " /> |
||||
|
|
||||
|
<!-- Include the newly-built files in the publish output --> |
||||
|
<ItemGroup> |
||||
|
<DistFiles Include="$(SpaRoot)dist\**; $(SpaRoot)dist-server\**" /> |
||||
|
<DistFiles Include="$(SpaRoot)node_modules\**" Condition="'$(BuildServerSideRenderer)' == 'true'" /> |
||||
|
<ResolvedFileToPublish Include="@(DistFiles->'%(FullPath)')" Exclude="@(ResolvedFileToPublish)"> |
||||
|
<RelativePath>%(DistFiles.Identity)</RelativePath> |
||||
|
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory> |
||||
|
</ResolvedFileToPublish> |
||||
|
</ItemGroup> |
||||
|
</Target> |
||||
|
|
||||
|
</Project> |
@ -0,0 +1,14 @@ |
|||||
|
using Discord.WebSocket; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace DiscoBot |
||||
|
{ |
||||
|
interface IModule |
||||
|
{ |
||||
|
Dictionary<string,Func<SocketMessage, string[], Task>> Commands { get; set; } |
||||
|
void Initialize(WSController wsC); |
||||
|
} |
||||
|
} |
@ -0,0 +1,26 @@ |
|||||
|
@page |
||||
|
@model ErrorModel |
||||
|
@{ |
||||
|
ViewData["Title"] = "Error"; |
||||
|
} |
||||
|
|
||||
|
<h1 class="text-danger">Error.</h1> |
||||
|
<h2 class="text-danger">An error occurred while processing your request.</h2> |
||||
|
|
||||
|
@if (Model.ShowRequestId) |
||||
|
{ |
||||
|
<p> |
||||
|
<strong>Request ID:</strong> <code>@Model.RequestId</code> |
||||
|
</p> |
||||
|
} |
||||
|
|
||||
|
<h3>Development Mode</h3> |
||||
|
<p> |
||||
|
Swapping to the <strong>Development</strong> environment displays detailed information about the error that occurred. |
||||
|
</p> |
||||
|
<p> |
||||
|
<strong>The Development environment shouldn't be enabled for deployed applications.</strong> |
||||
|
It can result in displaying sensitive information from exceptions to end users. |
||||
|
For local debugging, enable the <strong>Development</strong> environment by setting the <strong>ASPNETCORE_ENVIRONMENT</strong> environment variable to <strong>Development</strong> |
||||
|
and restarting the app. |
||||
|
</p> |
@ -0,0 +1,23 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Diagnostics; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.Mvc.RazorPages; |
||||
|
|
||||
|
namespace DiscoBot.Pages |
||||
|
{ |
||||
|
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] |
||||
|
public class ErrorModel : PageModel |
||||
|
{ |
||||
|
public string RequestId { get; set; } |
||||
|
|
||||
|
public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); |
||||
|
|
||||
|
public void OnGet() |
||||
|
{ |
||||
|
RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,3 @@ |
|||||
|
@using DiscoBot |
||||
|
@namespace DiscoBot.Pages |
||||
|
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers |
@ -0,0 +1,24 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.IO; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore; |
||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
|
||||
|
namespace DiscoBot |
||||
|
{ |
||||
|
public class Program |
||||
|
{ |
||||
|
public static void Main(string[] args) |
||||
|
{ |
||||
|
CreateWebHostBuilder(args).Build().Run(); |
||||
|
} |
||||
|
|
||||
|
public static IWebHostBuilder CreateWebHostBuilder(string[] args) => |
||||
|
WebHost.CreateDefaultBuilder(args) |
||||
|
.UseStartup<Startup>(); |
||||
|
} |
||||
|
} |
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"profiles": { |
||||
|
"DiscoBot": { |
||||
|
"commandName": "Project", |
||||
|
"launchBrowser": false, |
||||
|
"applicationUrl": "http://localhost:5000", |
||||
|
"environmentVariables": { |
||||
|
"ASPNETCORE_ENVIRONMENT": "Development" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,114 @@ |
|||||
|
using Microsoft.AspNetCore.Builder; |
||||
|
using Microsoft.AspNetCore.Hosting; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.AspNetCore.SpaServices.AngularCli; |
||||
|
using Microsoft.Extensions.Configuration; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Hosting; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Net.WebSockets; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace DiscoBot |
||||
|
{ |
||||
|
public class Startup |
||||
|
{ |
||||
|
private List<WebSocket> webSockets = new List<WebSocket>(); |
||||
|
private WSController wsC = new WSController(); |
||||
|
private DisBot diBot; |
||||
|
public Startup(IConfiguration configuration) |
||||
|
{ |
||||
|
Configuration = configuration; |
||||
|
} |
||||
|
|
||||
|
public IConfiguration Configuration { get; } |
||||
|
|
||||
|
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
|
public void ConfigureServices(IServiceCollection services) |
||||
|
{ |
||||
|
services.AddMvc() |
||||
|
.AddNewtonsoftJson(); |
||||
|
|
||||
|
// In production, the Angular files will be served from this directory
|
||||
|
services.AddSpaStaticFiles(configuration => |
||||
|
{ |
||||
|
configuration.RootPath = "ClientApp/dist"; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
|
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) |
||||
|
{ |
||||
|
diBot = new DisBot(wsC); |
||||
|
Task.Run(() => |
||||
|
{ |
||||
|
diBot.Initialize(); |
||||
|
}); |
||||
|
if (env.IsDevelopment()) |
||||
|
{ |
||||
|
app.UseDeveloperExceptionPage(); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
app.UseExceptionHandler("/Error"); |
||||
|
} |
||||
|
|
||||
|
app.UseStaticFiles(); |
||||
|
|
||||
|
var webSocketOptions = new WebSocketOptions() |
||||
|
{ |
||||
|
KeepAliveInterval = TimeSpan.FromSeconds(120), |
||||
|
ReceiveBufferSize = 4 * 1024 |
||||
|
}; |
||||
|
|
||||
|
app.UseWebSockets(webSocketOptions); |
||||
|
|
||||
|
|
||||
|
|
||||
|
app.Use(async (context, next) => |
||||
|
{ |
||||
|
if (context.Request.Path == "/ws") |
||||
|
{ |
||||
|
if (context.WebSockets.IsWebSocketRequest) |
||||
|
{ |
||||
|
WebSocket webSocket = await context.WebSockets.AcceptWebSocketAsync(); |
||||
|
webSockets.Add(webSocket); |
||||
|
//await Echo(context, webSocket);
|
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
context.Response.StatusCode = 400; |
||||
|
} |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
await next(); |
||||
|
} |
||||
|
|
||||
|
}); |
||||
|
|
||||
|
app.UseSpaStaticFiles(); |
||||
|
|
||||
|
app.UseMvc(routes => |
||||
|
{ |
||||
|
routes.MapRoute( |
||||
|
name: "default", |
||||
|
template: "{controller}/{action=Index}/{id?}"); |
||||
|
}); |
||||
|
|
||||
|
app.UseSpa(spa => |
||||
|
{ |
||||
|
// To learn more about options for serving an Angular SPA from ASP.NET Core,
|
||||
|
// see https://go.microsoft.com/fwlink/?linkid=864501
|
||||
|
|
||||
|
spa.Options.SourcePath = "ClientApp"; |
||||
|
|
||||
|
if (env.IsDevelopment()) |
||||
|
{ |
||||
|
spa.UseAngularCliServer(npmScript: "start"); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,11 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace DiscoBot |
||||
|
{ |
||||
|
public class WSController |
||||
|
{ |
||||
|
} |
||||
|
} |
@ -0,0 +1,9 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Debug", |
||||
|
"System": "Information", |
||||
|
"Microsoft": "Information" |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,8 @@ |
|||||
|
{ |
||||
|
"Logging": { |
||||
|
"LogLevel": { |
||||
|
"Default": "Warning" |
||||
|
} |
||||
|
}, |
||||
|
"AllowedHosts": "*" |
||||
|
} |
@ -0,0 +1,42 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Microsoft.AspNetCore.Http; |
||||
|
using Microsoft.AspNetCore.Mvc; |
||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
|
||||
|
namespace DiscoBot.calendar |
||||
|
{ |
||||
|
[Route("api/[controller]")]
|
||||
|
[ApiController] |
||||
|
public class CalController : Controller |
||||
|
{ |
||||
|
// GET: api/Cal
|
||||
|
[HttpGet("{guild}", Name = "Get")] |
||||
|
public JsonResult Get(ulong guild) |
||||
|
{ |
||||
|
CalendarContext calendarContext = new CalendarContext(guild); |
||||
|
List<CalendarItem> items = calendarContext.CalendarItems.Include(i => i.Attendance).ToList(); |
||||
|
return Json(items); |
||||
|
} |
||||
|
|
||||
|
// POST: api/Cal
|
||||
|
[HttpPost] |
||||
|
public void Post([FromBody] string value) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
// PUT: api/Cal/5
|
||||
|
[HttpPut("{id}")] |
||||
|
public void Put(int id, [FromBody] string value) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
// DELETE: api/ApiWithActions/5
|
||||
|
[HttpDelete("{id}")] |
||||
|
public void Delete(int id) |
||||
|
{ |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,76 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using Discord; |
||||
|
using Discord.WebSocket; |
||||
|
|
||||
|
namespace DiscoBot.calendar |
||||
|
{ |
||||
|
public class Calendar : IModule |
||||
|
{ |
||||
|
private WSController wsC; |
||||
|
private SocketGuild guild; |
||||
|
public enum Attendance { Attending, Maybe } |
||||
|
private CalendarContext calendarContext; |
||||
|
public Dictionary<string, Func<SocketMessage, string[], Task>> Commands { get; set; } = new Dictionary<string, Func<SocketMessage, string[], Task>>(); |
||||
|
|
||||
|
public Calendar(SocketGuild guild) |
||||
|
{ |
||||
|
this.guild = guild; |
||||
|
calendarContext = new CalendarContext(guild.Id); |
||||
|
calendarContext.Database.EnsureCreated(); |
||||
|
|
||||
|
Commands.Add("caltest", HandleTestCommand); |
||||
|
Commands.Add("caladd", HandleAddEventCommand); |
||||
|
} |
||||
|
|
||||
|
public void Initialize(WSController wsC) |
||||
|
{ |
||||
|
this.wsC = wsC; |
||||
|
Console.WriteLine("Initializing calendar..."); |
||||
|
} |
||||
|
|
||||
|
private Task HandleTestCommand(SocketMessage msg, string[] parameters) |
||||
|
{ |
||||
|
Console.WriteLine("Calendar: Handling test command!"); |
||||
|
string para = string.Join(",", parameters); |
||||
|
msg.Channel.SendMessageAsync("Test succeeded. Params: " + para); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task HandleAddEventCommand(SocketMessage msg, string[] parameters) |
||||
|
{ |
||||
|
if(parameters.Length != 4) |
||||
|
{ |
||||
|
msg.Channel.SendMessageAsync("Usage: caladd Name Date Comment"); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
try |
||||
|
{ |
||||
|
var gchan = msg.Channel as IGuildChannel; |
||||
|
string name = parameters[1]; |
||||
|
string date = parameters[2]; |
||||
|
string comment = parameters[3]; |
||||
|
CalendarItem calItem = new CalendarItem(); |
||||
|
calItem.Channel = gchan.Id; |
||||
|
calItem.Name = name; |
||||
|
|
||||
|
calItem.Date = DateTimeOffset.Parse(date); |
||||
|
calItem.Comment = comment; |
||||
|
calItem.Attendance = new List<CalendarItemAttendance>(); |
||||
|
var attendance = new CalendarItemAttendance(); |
||||
|
attendance.Attendee = msg.Author.Id; |
||||
|
attendance.Attending = Attendance.Attending; |
||||
|
calItem.Attendance.Add(attendance); |
||||
|
calendarContext.CalendarItems.Add(calItem); |
||||
|
calendarContext.SaveChanges(); |
||||
|
} |
||||
|
catch(FormatException) |
||||
|
{ |
||||
|
msg.Channel.SendMessageAsync("Your date input was invalid. Try again with a valid date."); |
||||
|
} |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,46 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
using static DiscoBot.calendar.Calendar; |
||||
|
|
||||
|
namespace DiscoBot.calendar |
||||
|
{ |
||||
|
public class CalendarContext : DbContext |
||||
|
{ |
||||
|
private ulong guildId; |
||||
|
public DbSet<CalendarItem> CalendarItems { get; set; } |
||||
|
|
||||
|
public CalendarContext(ulong guildId) |
||||
|
{ |
||||
|
this.guildId = guildId; |
||||
|
} |
||||
|
|
||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) |
||||
|
{ |
||||
|
optionsBuilder.UseSqlite("Data Source=" + guildId + "-calendar.db"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class CalendarItemAttendance |
||||
|
{ |
||||
|
[Key] |
||||
|
public ulong Id { get; set; } |
||||
|
public ulong Attendee { get; set; } |
||||
|
public Attendance Attending { get; set; } |
||||
|
} |
||||
|
|
||||
|
|
||||
|
public class CalendarItem |
||||
|
{ |
||||
|
[Key] |
||||
|
public ulong Id { get; set; } |
||||
|
public string Name { get; set; } |
||||
|
public ulong Channel { get; set; } |
||||
|
public DateTimeOffset Date { get; set; } |
||||
|
public string Comment { get; set; } |
||||
|
public List<CalendarItemAttendance> Attendance { get; set; } |
||||
|
} |
||||
|
} |
@ -0,0 +1,208 @@ |
|||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.Linq; |
||||
|
using System.ServiceModel.Syndication; |
||||
|
using System.Threading.Tasks; |
||||
|
using System.Timers; |
||||
|
using System.Xml; |
||||
|
using Discord; |
||||
|
using Discord.WebSocket; |
||||
|
|
||||
|
namespace DiscoBot.rss |
||||
|
{ |
||||
|
public class Rss : IModule |
||||
|
{ |
||||
|
private SocketGuild guild; |
||||
|
private RssContext rssContext; |
||||
|
private Dictionary<string, Timer> timers = new Dictionary<string, Timer>(); |
||||
|
|
||||
|
public Dictionary<string, Func<SocketMessage, string[], Task>> Commands { get; set; } = new Dictionary<string, Func<SocketMessage, string[], Task>>(); |
||||
|
|
||||
|
public Rss(SocketGuild guild) |
||||
|
{ |
||||
|
this.guild = guild; |
||||
|
rssContext = new RssContext(guild.Id); |
||||
|
rssContext.Database.EnsureCreated(); |
||||
|
foreach(var f in rssContext.RssFeeds) |
||||
|
{ |
||||
|
InitializeFeed(f); |
||||
|
} |
||||
|
|
||||
|
Commands.Add("test",HandleTestCommand); |
||||
|
Commands.Add("rssadd", HandleRssAddCommand); |
||||
|
Commands.Add("rssdel", HandleRssDelCommand); |
||||
|
Commands.Add("rsslist", HandleRssListCommand); |
||||
|
Commands.Add("rsslistall", HandleRssListAllCommand); |
||||
|
} |
||||
|
|
||||
|
private Task InitializeFeed(RssFeed feed) |
||||
|
{ |
||||
|
Console.WriteLine("Found feed " + feed.Name); |
||||
|
Timer timer = new Timer(feed.CheckInterval.TotalMilliseconds); |
||||
|
timer.AutoReset = true; |
||||
|
timer.Elapsed += async (sender, e) => await HandleFeedCheck(feed); |
||||
|
timer.Start(); |
||||
|
timers.Add(feed.Name, timer); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task DeinitializeFeed(RssFeed feed) |
||||
|
{ |
||||
|
Timer t = timers[feed.Name]; |
||||
|
t.Stop(); |
||||
|
timers.Remove(feed.Name); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task HandleFeedCheck(RssFeed feed) |
||||
|
{ |
||||
|
SocketTextChannel c = guild.Channels.Where(g => g.Id == feed.Channel).Single() as SocketTextChannel; |
||||
|
try |
||||
|
{ |
||||
|
XmlReader reader = XmlReader.Create(feed.Url); |
||||
|
SyndicationFeed f = SyndicationFeed.Load(reader); |
||||
|
DateTimeOffset newestItem = feed.LastChecked; |
||||
|
foreach(var item in f.Items) |
||||
|
{ |
||||
|
if (item.LastUpdatedTime <= feed.LastChecked) |
||||
|
continue; |
||||
|
Console.WriteLine("{0}: {1} | {2}", feed.Name, item.LastUpdatedTime, feed.LastChecked); |
||||
|
List<string> m = new List<string>(); |
||||
|
m.Add("<" + feed.Name + "> " + item.Title.Text); |
||||
|
if(item.LastUpdatedTime > newestItem) |
||||
|
{ |
||||
|
Console.WriteLine("^-- UPDATE!"); |
||||
|
newestItem = item.LastUpdatedTime; |
||||
|
} |
||||
|
foreach (var l in item.Links) |
||||
|
{ |
||||
|
m.Add(l.Uri.ToString()); |
||||
|
} |
||||
|
c.SendMessageAsync(string.Join(" | ", m)); |
||||
|
} |
||||
|
feed.LastChecked = newestItem; |
||||
|
rssContext.SaveChangesAsync(); |
||||
|
} catch(System.Net.WebException e) |
||||
|
{ |
||||
|
c.SendMessageAsync("<" + feed.Name + "> " + "NetworkFailure: " + e.Message); |
||||
|
} catch(XmlException e) |
||||
|
{ |
||||
|
c.SendMessageAsync("<" + feed.Name + "> " + "Malformed Response: " + e.Message); |
||||
|
} |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task HandleRssAddCommand(SocketMessage msg, string[] parameters) |
||||
|
{ |
||||
|
var gchan = msg.Channel as IGuildChannel; |
||||
|
string name = parameters[1]; |
||||
|
string url = parameters[2]; |
||||
|
try |
||||
|
{ |
||||
|
int timeSec = Int32.Parse(parameters[3]); |
||||
|
RssFeed feed = new RssFeed(); |
||||
|
rssContext.RssFeeds.Add(feed); |
||||
|
feed.Name = name; |
||||
|
feed.Url = url; |
||||
|
feed.Channel = gchan.Id; |
||||
|
feed.CheckInterval = new TimeSpan(0, 0, timeSec); |
||||
|
feed.LastChecked = DateTimeOffset.Now; |
||||
|
try |
||||
|
{ |
||||
|
rssContext.SaveChanges(); |
||||
|
InitializeFeed(feed); |
||||
|
msg.Channel.SendMessageAsync("Feed " + feed.Name + " with url " + feed.Url + " saved."); |
||||
|
} |
||||
|
catch (InvalidOperationException) |
||||
|
{ |
||||
|
msg.Channel.SendMessageAsync("Unable to save rss feed."); |
||||
|
} |
||||
|
} |
||||
|
catch (FormatException) |
||||
|
{ |
||||
|
msg.Channel.SendMessageAsync("Unable to save rss feed. Invalid check time."); |
||||
|
} |
||||
|
|
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task HandleRssListCommand(SocketMessage msg, string[] parameters) |
||||
|
{ |
||||
|
List<RssFeed> feeds = rssContext.RssFeeds.Where(f => f.Channel == msg.Channel.Id).ToList(); |
||||
|
List<string> m = new List<string>(); |
||||
|
m.Add("Feeds for this channel are:"); |
||||
|
m.Add("Name | URL | CheckInterval | LastChecked"); |
||||
|
msg.Channel.SendMessageAsync(string.Join("\n", m)); |
||||
|
m = new List<string>(); |
||||
|
int cnt = 0; |
||||
|
foreach (var f in feeds) |
||||
|
{ |
||||
|
m.Add("- " + f.Name + " | " + f.Url + " | " + f.CheckInterval + " | " + f.LastChecked); |
||||
|
cnt++; |
||||
|
if(cnt > 4) |
||||
|
{ |
||||
|
cnt = 0; |
||||
|
msg.Channel.SendMessageAsync(string.Join("\n", m)); |
||||
|
} |
||||
|
} |
||||
|
if(cnt > 0) |
||||
|
msg.Channel.SendMessageAsync(string.Join("\n", m)); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task HandleRssListAllCommand(SocketMessage msg, string[] parameters) |
||||
|
{ |
||||
|
List<RssFeed> feeds = rssContext.RssFeeds.ToList(); |
||||
|
List<string> m = new List<string>(); |
||||
|
m.Add("Feeds for this guild are:"); |
||||
|
m.Add("Name | URL | Channel | CheckInterval | LastChecked"); |
||||
|
msg.Channel.SendMessageAsync(string.Join("\n", m)); |
||||
|
m = new List<string>(); |
||||
|
int cnt = 0; |
||||
|
foreach (var f in feeds) |
||||
|
{ |
||||
|
m.Add("- " + f.Name + " | " + f.Url + " | " + guild.Channels.Where(c => c.Id == f.Channel).Single() + " | " + f.CheckInterval + " | " + f.LastChecked); |
||||
|
cnt++; |
||||
|
if (cnt > 4) |
||||
|
{ |
||||
|
cnt = 0; |
||||
|
msg.Channel.SendMessageAsync(string.Join("\n", m)); |
||||
|
} |
||||
|
} |
||||
|
if(cnt > 0) |
||||
|
msg.Channel.SendMessageAsync(string.Join("\n", m)); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task HandleRssDelCommand(SocketMessage msg, string[] parameters) |
||||
|
{ |
||||
|
var gchan = msg.Channel as IGuildChannel; |
||||
|
string name = parameters[1]; |
||||
|
try |
||||
|
{ |
||||
|
RssFeed f = rssContext.RssFeeds.Where(f => f.Name == name).Single(); |
||||
|
rssContext.Remove(f); |
||||
|
rssContext.SaveChanges(); |
||||
|
DeinitializeFeed(f); |
||||
|
msg.Channel.SendMessageAsync("Removed feed " + f.Name); |
||||
|
} catch(InvalidOperationException) |
||||
|
{ |
||||
|
msg.Channel.SendMessageAsync("Could not find feed " + name); |
||||
|
} |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
private Task HandleTestCommand(SocketMessage msg, string[] parameters) |
||||
|
{ |
||||
|
Console.WriteLine("Handling test command!"); |
||||
|
string para = string.Join(",", parameters); |
||||
|
msg.Channel.SendMessageAsync("Test succeeded. Params: "+para); |
||||
|
return Task.CompletedTask; |
||||
|
} |
||||
|
|
||||
|
public void Initialize(WSController wsC) |
||||
|
{ |
||||
|
Console.WriteLine("Initializing rss..."); |
||||
|
} |
||||
|
} |
||||
|
} |
@ -0,0 +1,36 @@ |
|||||
|
using Microsoft.EntityFrameworkCore; |
||||
|
using System; |
||||
|
using System.Collections.Generic; |
||||
|
using System.ComponentModel.DataAnnotations; |
||||
|
using System.Linq; |
||||
|
using System.Threading.Tasks; |
||||
|
|
||||
|
namespace DiscoBot.rss |
||||
|
{ |
||||
|
public class RssContext : DbContext |
||||
|
{ |
||||
|
private ulong guildId; |
||||
|
public DbSet<RssFeed> RssFeeds { get; set; } |
||||
|
|
||||
|
public RssContext(ulong guildId) |
||||
|
{ |
||||
|
this.guildId = guildId; |
||||
|
} |
||||
|
|
||||
|
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) |
||||
|
{ |
||||
|
optionsBuilder.UseSqlite("Data Source=" + guildId + "-rss.db"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public class RssFeed |
||||
|
{ |
||||
|
[Key] |
||||
|
public string Name { get; set; } |
||||
|
public string Url { get; set; } |
||||
|
public ulong Channel { get; set; } |
||||
|
public TimeSpan CheckInterval { get; set; } |
||||
|
public DateTimeOffset LastChecked { get; set; } |
||||
|
|
||||
|
} |
||||
|
} |
After Width: | Height: | Size: 31 KiB |
Loading…
Reference in new issue