Skip to content

Commit f828b43

Browse files
committed
feature: basic dialogflow chatbot
1 parent 61a74fd commit f828b43

18 files changed

+254
-55
lines changed

.angular-cli.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"test": "test.ts",
1818
"tsconfig": "tsconfig.app.json",
1919
"testTsconfig": "tsconfig.spec.json",
20-
"prefix": "app",
20+
"prefix": "",
2121
"styles": [
2222
"styles.scss"
2323
],

.gitignore

+3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# See http://help.github.com/ignore-files/ for more about ignoring files.
22

3+
/src/environments/environment.prod.ts
4+
/src/environments/environment.ts
5+
36
# compiled output
47
/dist
58
/tmp

README.md

+9-20
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,16 @@
1-
# Chatbot
1+
# Angular Chatbot with DialogFlow
22

3-
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.4.9.
3+
Watch the screencast:
44

5-
## Development server
5+
## Usage
66

7-
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.
7+
First, create an agent on DialogFlow.
88

9-
## Code scaffolding
9+
- git clone
10+
- create `/src/environments/environment.ts` with your API client token
11+
- npm install
12+
- ng serve
1013

11-
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`.
1214

13-
## Build
1415

15-
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.
16-
17-
## Running unit tests
18-
19-
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
20-
21-
## Running end-to-end tests
22-
23-
Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/).
24-
25-
## Further help
26-
27-
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).
16+
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 1.4.9.

package-lock.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+12-11
Original file line numberDiff line numberDiff line change
@@ -12,26 +12,27 @@
1212
},
1313
"private": true,
1414
"dependencies": {
15-
"@angular/animations": "^4.2.4",
16-
"@angular/common": "^4.2.4",
17-
"@angular/compiler": "^4.2.4",
18-
"@angular/core": "^4.2.4",
19-
"@angular/forms": "^4.2.4",
20-
"@angular/http": "^4.2.4",
21-
"@angular/platform-browser": "^4.2.4",
22-
"@angular/platform-browser-dynamic": "^4.2.4",
23-
"@angular/router": "^4.2.4",
15+
"@angular/animations": "^4.4.6",
16+
"@angular/common": "^4.4.6",
17+
"@angular/compiler": "^4.4.6",
18+
"@angular/core": "^4.4.6",
19+
"@angular/forms": "^4.4.6",
20+
"@angular/http": "^4.4.6",
21+
"@angular/platform-browser": "^4.4.6",
22+
"@angular/platform-browser-dynamic": "^4.4.6",
23+
"@angular/router": "^4.4.6",
2424
"core-js": "^2.4.1",
2525
"rxjs": "^5.4.2",
2626
"zone.js": "^0.8.14"
2727
},
2828
"devDependencies": {
2929
"@angular/cli": "1.4.9",
30-
"@angular/compiler-cli": "^4.2.4",
31-
"@angular/language-service": "^4.2.4",
30+
"@angular/compiler-cli": "^4.4.6",
31+
"@angular/language-service": "^4.4.6",
3232
"@types/jasmine": "~2.5.53",
3333
"@types/jasminewd2": "~2.0.2",
3434
"@types/node": "~6.0.60",
35+
"api-ai-javascript": "^2.0.0-beta.21",
3536
"codelyzer": "~3.2.0",
3637
"jasmine-core": "~2.6.2",
3738
"jasmine-spec-reporter": "~4.1.0",

src/app/app.component.html

+9-19
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,10 @@
1-
<!--The content below is only a placeholder and can be replaced.-->
2-
<div style="text-align:center">
3-
<h1>
4-
Welcome to {{title}}!
5-
</h1>
6-
<img width="300" src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTAgMjUwIj4KICAgIDxwYXRoIGZpbGw9IiNERDAwMzEiIGQ9Ik0xMjUgMzBMMzEuOSA2My4ybDE0LjIgMTIzLjFMMTI1IDIzMGw3OC45LTQzLjcgMTQuMi0xMjMuMXoiIC8+CiAgICA8cGF0aCBmaWxsPSIjQzMwMDJGIiBkPSJNMTI1IDMwdjIyLjItLjFWMjMwbDc4LjktNDMuNyAxNC4yLTEyMy4xTDEyNSAzMHoiIC8+CiAgICA8cGF0aCAgZmlsbD0iI0ZGRkZGRiIgZD0iTTEyNSA1Mi4xTDY2LjggMTgyLjZoMjEuN2wxMS43LTI5LjJoNDkuNGwxMS43IDI5LjJIMTgzTDEyNSA1Mi4xem0xNyA4My4zaC0zNGwxNy00MC45IDE3IDQwLjl6IiAvPgogIDwvc3ZnPg==">
7-
</div>
8-
<h2>Here are some links to help you start: </h2>
9-
<ul>
10-
<li>
11-
<h2><a target="_blank" rel="noopener" href="https://angular.io/tutorial">Tour of Heroes</a></h2>
12-
</li>
13-
<li>
14-
<h2><a target="_blank" rel="noopener" href="https://github.com/angular/angular-cli/wiki">CLI Documentation</a></h2>
15-
</li>
16-
<li>
17-
<h2><a target="_blank" rel="noopener" href="https://blog.angular.io/">Angular blog</a></h2>
18-
</li>
19-
</ul>
1+
<div class="row">
2+
<div class="column"></div>
3+
<div class="column column-50">
4+
<chat-dialog></chat-dialog>
5+
</div>
6+
<div class="column"></div>
7+
</div>
8+
9+
2010

src/app/app.module.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,15 @@ import { NgModule } from '@angular/core';
33

44
import { AppComponent } from './app.component';
55

6+
import { ChatModule } from './chat/chat.module';
7+
68
@NgModule({
79
declarations: [
810
AppComponent
911
],
1012
imports: [
11-
BrowserModule
13+
BrowserModule,
14+
ChatModule
1215
],
1316
providers: [],
1417
bootstrap: [AppComponent]

src/app/chat.service.spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { TestBed, inject } from '@angular/core/testing';
2+
3+
import { ChatService } from './chat.service';
4+
5+
describe('ChatService', () => {
6+
beforeEach(() => {
7+
TestBed.configureTestingModule({
8+
providers: [ChatService]
9+
});
10+
});
11+
12+
it('should be created', inject([ChatService], (service: ChatService) => {
13+
expect(service).toBeTruthy();
14+
}));
15+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<h1>Angular Bot</h1>
2+
3+
4+
5+
<ng-container *ngFor="let message of messages | async">
6+
7+
<div class="message" [ngClass]="{ 'from': message.sentBy === 'bot',
8+
'to': message.sentBy === 'user' }">
9+
{{ message.content }}
10+
</div>
11+
12+
</ng-container>
13+
14+
15+
<label for="nameField">Your Message</label>
16+
17+
18+
<input [(ngModel)]="formValue" (keyup.enter)="sendMessage()" type="text"><br>
19+
20+
<button (click)="sendMessage()">Send</button>
21+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
.message {
2+
border-radius: 50px;
3+
margin: 0 15px 10px;
4+
padding: 15px 20px;
5+
position: relative;
6+
font-weight: bold;
7+
}
8+
.message.to {
9+
background-color: #2095FE;
10+
color: #fff;
11+
margin-left: 100px;
12+
text-align: right;
13+
}
14+
.message.from {
15+
background-color: #E5E4E9;
16+
color: #363636;
17+
margin-right: 100px;
18+
19+
}
20+
.message.to + .message.to,
21+
.message.from + .message.from {
22+
margin-top: -10px;
23+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { ChatDialogComponent } from './chat-dialog.component';
4+
5+
describe('ChatDialogComponent', () => {
6+
let component: ChatDialogComponent;
7+
let fixture: ComponentFixture<ChatDialogComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ ChatDialogComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(ChatDialogComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import { Component, OnInit } from '@angular/core';
2+
import { ChatService, Message } from '../chat.service';
3+
import { Observable } from 'rxjs/Observable';
4+
import 'rxjs/add/operator/scan';
5+
6+
7+
@Component({
8+
selector: 'chat-dialog',
9+
templateUrl: './chat-dialog.component.html',
10+
styleUrls: ['./chat-dialog.component.scss']
11+
})
12+
export class ChatDialogComponent implements OnInit {
13+
14+
messages: Observable<Message[]>;
15+
formValue: string;
16+
17+
constructor(public chat: ChatService) { }
18+
19+
ngOnInit() {
20+
// appends to array after each new message is added to feedSource
21+
this.messages = this.chat.conversation.asObservable()
22+
.scan((acc, val) => acc.concat(val) );
23+
}
24+
25+
sendMessage() {
26+
this.chat.converse(this.formValue);
27+
this.formValue = '';
28+
}
29+
30+
}

src/app/chat/chat.module.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { NgModule } from '@angular/core';
2+
import { CommonModule } from '@angular/common';
3+
4+
import { ChatDialogComponent } from './chat-dialog/chat-dialog.component';
5+
import { ChatService } from './chat.service';
6+
7+
import { FormsModule } from '@angular/forms';
8+
9+
10+
@NgModule({
11+
imports: [
12+
CommonModule,
13+
FormsModule
14+
],
15+
declarations: [
16+
ChatDialogComponent
17+
],
18+
exports: [ ChatDialogComponent ],
19+
providers: [ChatService]
20+
})
21+
export class ChatModule { }

src/app/chat/chat.service.spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { TestBed, inject } from '@angular/core/testing';
2+
3+
import { ChatService } from './chat.service';
4+
5+
describe('ChatService', () => {
6+
beforeEach(() => {
7+
TestBed.configureTestingModule({
8+
providers: [ChatService]
9+
});
10+
});
11+
12+
it('should be created', inject([ChatService], (service: ChatService) => {
13+
expect(service).toBeTruthy();
14+
}));
15+
});

src/app/chat/chat.service.ts

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { Injectable } from '@angular/core';
2+
import { environment } from '../../environments/environment';
3+
4+
import { ApiAiClient } from 'api-ai-javascript';
5+
6+
import { Observable } from 'rxjs/Observable';
7+
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
8+
9+
export class Message {
10+
constructor(public content: string, public sentBy: string) {}
11+
}
12+
13+
@Injectable()
14+
export class ChatService {
15+
16+
readonly token = environment.dialogflow.angularBot;
17+
readonly client = new ApiAiClient({ accessToken: this.token });
18+
19+
conversation = new BehaviorSubject<Message[]>([]);
20+
21+
constructor() {}
22+
23+
// Sends and receives messages via DialogFlow
24+
converse(msg: string) {
25+
const userMessage = new Message(msg, 'user');
26+
this.update(userMessage);
27+
28+
return this.client.textRequest(msg)
29+
.then(res => {
30+
const speech = res.result.fulfillment.speech;
31+
const botMessage = new Message(speech, 'bot');
32+
this.update(botMessage);
33+
});
34+
}
35+
36+
37+
38+
// Adds message to source
39+
update(msg: Message) {
40+
this.conversation.next([msg]);
41+
}
42+
43+
44+
}

src/environments/environment.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,9 @@
44
// The list of which env maps to which file can be found in `.angular-cli.json`.
55

66
export const environment = {
7-
production: false
7+
production: false,
8+
9+
dialogflow: {
10+
angularBot: '453318da34424e97be81f1bc549521e0'
11+
}
812
};

src/index.html

+9
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77

88
<meta name="viewport" content="width=device-width, initial-scale=1">
99
<link rel="icon" type="image/x-icon" href="favicon.ico">
10+
<link rel="stylesheet" href="//fonts.googleapis.com/css?family=Roboto:300,300italic,700,700italic">
11+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
12+
13+
14+
<!-- CSS Reset -->
15+
<link rel="stylesheet" href="//cdn.rawgit.com/necolas/normalize.css/master/normalize.css">
16+
17+
<!-- Milligram CSS minified -->
18+
<link rel="stylesheet" href="//cdn.rawgit.com/milligram/milligram/master/dist/milligram.min.css">
1019
</head>
1120
<body>
1221
<app-root></app-root>

tslint.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,13 @@
118118
"directive-selector": [
119119
true,
120120
"attribute",
121-
"app",
121+
"",
122122
"camelCase"
123123
],
124124
"component-selector": [
125125
true,
126126
"element",
127-
"app",
127+
"",
128128
"kebab-case"
129129
],
130130
"use-input-property-decorator": true,

0 commit comments

Comments
 (0)