Skip to content

Commit cd15b24

Browse files
authored
Merge pull request #76 from line/feature/kitchensink
Add kitchensink example
2 parents 4c14200 + 60eaa50 commit cd15b24

File tree

1 file changed

+391
-0
lines changed

1 file changed

+391
-0
lines changed

examples/kitchensink/app.rb

Lines changed: 391 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
require 'sinatra' # gem 'sinatra'
2+
require 'line/bot' # gem 'line-bot-api'
3+
4+
THUMBNAIL_URL = 'https://via.placeholder.com/1024x1024'
5+
HORIZONTAL_THUMBNAIL_URL = 'https://via.placeholder.com/1024x768'
6+
7+
def client
8+
@client ||= Line::Bot::Client.new do |config|
9+
config.channel_secret = ENV["LINE_CHANNEL_SECRET"]
10+
config.channel_token = ENV["LINE_CHANNEL_TOKEN"]
11+
end
12+
end
13+
14+
def reply_text(event, texts)
15+
texts = [texts] if texts.is_a?(String)
16+
client.reply_message(
17+
event['replyToken'],
18+
texts.map { |text| {type: 'text', text: text} }
19+
)
20+
end
21+
22+
def reply_content(event, messages)
23+
res = client.reply_message(
24+
event['replyToken'],
25+
messages
26+
)
27+
puts res.read_body if res.code != 200
28+
end
29+
30+
post '/callback' do
31+
body = request.body.read
32+
33+
signature = request.env['HTTP_X_LINE_SIGNATURE']
34+
unless client.validate_signature(body, signature)
35+
error 400 do 'Bad Request' end
36+
end
37+
38+
events = client.parse_events_from(body)
39+
40+
events.each do |event|
41+
case event
42+
when Line::Bot::Event::Message
43+
handle_message(event)
44+
45+
when Line::Bot::Event::Follow
46+
reply_text(event, "[FOLLOW]\nThank you for following")
47+
48+
when Line::Bot::Event::Unfollow
49+
puts "[UNFOLLOW]\n#{body}"
50+
51+
when Line::Bot::Event::Join
52+
reply_text(event, "[JOIN]\n#{event['source']['type']}")
53+
54+
when Line::Bot::Event::Leave
55+
puts "[LEAVE]\n#{body}"
56+
57+
when Line::Bot::Event::Postback
58+
message = "[POSTBACK]\n#{event['postback']['data']} (#{JSON.generate(event['postback']['params'])})"
59+
reply_text(event, message)
60+
61+
when Line::Bot::Event::Beacon
62+
reply_text(event, "[BEACON]\n#{JSON.generate(event['beacon'])}")
63+
64+
else
65+
reply_text(event, "Unknown event type: #{event}")
66+
end
67+
end
68+
69+
"OK"
70+
end
71+
72+
def handle_message(event)
73+
case event.type
74+
when Line::Bot::Event::MessageType::Sticker
75+
handle_sticker(event)
76+
when Line::Bot::Event::MessageType::Location
77+
handle_location(event)
78+
when Line::Bot::Event::MessageType::Text
79+
case event.message['text']
80+
when 'profile'
81+
if event['source']['type'] == 'user'
82+
profile = client.get_profile(event['source']['userId'])
83+
profile = JSON.parse(profile.read_body)
84+
reply_text(event, [
85+
"Display name\n#{profile['displayName']}",
86+
"Status message\n#{profile['statusMessage']}"
87+
])
88+
else
89+
reply_text(event, "Bot can't use profile API without user ID")
90+
end
91+
92+
when 'buttons'
93+
reply_content(event, {
94+
type: 'template',
95+
altText: 'Buttons alt text',
96+
template: {
97+
type: 'buttons',
98+
thumbnailImageUrl: THUMBNAIL_URL,
99+
title: 'My button sample',
100+
text: 'Hello, my button',
101+
actions: [
102+
{ label: 'Go to line.me', type: 'uri', uri: 'https://line.me' },
103+
{ label: 'Send postback', type: 'postback', data: 'hello world' },
104+
{ label: 'Send postback2', type: 'postback', data: 'hello world', text: 'hello world' },
105+
{ label: 'Send message', type: 'message', text: 'This is message' }
106+
]
107+
}
108+
})
109+
110+
when 'confirm'
111+
reply_content(event, {
112+
type: 'template',
113+
altText: 'Confirm alt text',
114+
template: {
115+
type: 'confirm',
116+
text: 'Do it?',
117+
actions: [
118+
{ label: 'Yes', type: 'message', text: 'Yes!' },
119+
{ label: 'No', type: 'message', text: 'No!' },
120+
],
121+
}
122+
})
123+
124+
when 'carousel'
125+
reply_content(event, {
126+
type: 'template',
127+
altText: 'Carousel alt text',
128+
template: {
129+
type: 'carousel',
130+
columns: [
131+
{
132+
title: 'hoge',
133+
text: 'fuga',
134+
actions: [
135+
{ label: 'Go to line.me', type: 'uri', uri: 'https://line.me' },
136+
{ label: 'Send postback', type: 'postback', data: 'hello world' },
137+
{ label: 'Send message', type: 'message', text: 'This is message' }
138+
]
139+
},
140+
{
141+
title: 'Datetime Picker',
142+
text: 'Please select a date, time or datetime',
143+
actions: [
144+
{
145+
type: 'datetimepicker',
146+
label: "Datetime",
147+
data: 'action=sel',
148+
mode: 'datetime',
149+
initial: '2017-06-18T06:15',
150+
max: '2100-12-31T23:59',
151+
min: '1900-01-01T00:00'
152+
},
153+
{
154+
type: 'datetimepicker',
155+
label: "Date",
156+
data: 'action=sel&only=date',
157+
mode: 'date',
158+
initial: '2017-06-18',
159+
max: '2100-12-31',
160+
min: '1900-01-01'
161+
},
162+
{
163+
type: 'datetimepicker',
164+
label: "Time",
165+
data: 'action=sel&only=time',
166+
mode: 'time',
167+
initial: '12:15',
168+
max: '23:00',
169+
min: '10:00'
170+
}
171+
]
172+
}
173+
]
174+
}
175+
})
176+
177+
when 'image carousel'
178+
reply_content(event, {
179+
type: 'template',
180+
altText: 'Image carousel alt text',
181+
template: {
182+
type: 'image_carousel',
183+
columns: [
184+
{
185+
imageUrl: THUMBNAIL_URL,
186+
action: { label: 'line.me', type: 'uri', uri: 'https://line.me' }
187+
},
188+
{
189+
imageUrl: THUMBNAIL_URL,
190+
action: { label: 'postback', type: 'postback', data: 'hello world' }
191+
},
192+
{
193+
imageUrl: THUMBNAIL_URL,
194+
action: { label: 'message', type: 'message', text: 'This is message' }
195+
},
196+
{
197+
imageUrl: THUMBNAIL_URL,
198+
action: {
199+
type: 'datetimepicker',
200+
label: "Datetime",
201+
data: 'action=sel',
202+
mode: 'datetime',
203+
initial: '2017-06-18T06:15',
204+
max: '2100-12-31T23:59',
205+
min: '1900-01-01T00:00'
206+
}
207+
}
208+
]
209+
}
210+
})
211+
212+
when 'imagemap'
213+
reply_content(event, {
214+
type: 'imagemap',
215+
baseUrl: THUMBNAIL_URL,
216+
altText: 'Imagemap alt text',
217+
baseSize: { width: 1024, height: 1024 },
218+
actions: [
219+
{ area: { x: 0, y: 0, width: 512, height: 512 }, type: 'uri', linkUri: 'https://store.line.me/family/manga/en' },
220+
{ area: { x: 512, y: 0, width: 512, height: 512 }, type: 'uri', linkUri: 'https://store.line.me/family/music/en' },
221+
{ area: { x: 0, y: 512, width: 512, height: 512 }, type: 'uri', linkUri: 'https://store.line.me/family/play/en' },
222+
{ area: { x: 512, y: 512, width: 512, height: 512 }, type: 'message', text: 'Fortune!' },
223+
]
224+
})
225+
226+
when 'flex'
227+
reply_content(event, {
228+
type: "flex",
229+
altText: "this is a flex message",
230+
contents: {
231+
type: "bubble",
232+
header: {
233+
type: "box",
234+
layout: "vertical",
235+
contents: [
236+
{
237+
type: "text",
238+
text: "Header text"
239+
}
240+
]
241+
},
242+
hero: {
243+
type: "image",
244+
url: HORIZONTAL_THUMBNAIL_URL,
245+
size: "full",
246+
aspectRatio: "4:3"
247+
},
248+
body: {
249+
type: "box",
250+
layout: "vertical",
251+
contents: [
252+
{
253+
type: "text",
254+
text: "Body text",
255+
}
256+
]
257+
},
258+
footer: {
259+
type: "box",
260+
layout: "vertical",
261+
contents: [
262+
{
263+
type: "text",
264+
text: "Footer text",
265+
align: "center",
266+
color: "#888888"
267+
}
268+
]
269+
}
270+
}
271+
})
272+
273+
when 'flex carousel'
274+
reply_content(event, {
275+
type: "flex",
276+
altText: "this is a flex carousel",
277+
contents: {
278+
type: "carousel",
279+
contents: [
280+
{
281+
type: "bubble",
282+
body: {
283+
type: "box",
284+
layout: "horizontal",
285+
contents: [
286+
{
287+
type: "text",
288+
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
289+
wrap: true
290+
}
291+
]
292+
},
293+
footer: {
294+
type: "box",
295+
layout: "horizontal",
296+
contents: [
297+
{
298+
type: "button",
299+
style: "primary",
300+
action: {
301+
type: "uri",
302+
label: "Go",
303+
uri: "https://example.com"
304+
}
305+
}
306+
]
307+
}
308+
},
309+
{
310+
type: "bubble",
311+
body: {
312+
type: "box",
313+
layout: "horizontal",
314+
contents: [
315+
{
316+
type: "text",
317+
text: "Hello, World!",
318+
wrap: true
319+
}
320+
]
321+
},
322+
footer: {
323+
type: "box",
324+
layout: "horizontal",
325+
contents: [
326+
{
327+
type: "button",
328+
style: "primary",
329+
action: {
330+
type: "uri",
331+
label: "Go",
332+
uri: "https://example.com"
333+
}
334+
}
335+
]
336+
}
337+
}
338+
]
339+
}
340+
})
341+
342+
when 'bye'
343+
case event['source']['type']
344+
when 'user'
345+
reply_text(event, "[BYE]\nBot can't leave from 1:1 chat")
346+
when 'group'
347+
reply_text(event, "[BYE]\nLeaving group")
348+
client.leave_group(event['source']['groupId'])
349+
when 'room'
350+
reply_text(event, "[BYE]\nLeaving room")
351+
client.leave_room(event['source']['roomId'])
352+
end
353+
354+
else
355+
reply_text(event, "[ECHO]\n#{event.message['text']}")
356+
357+
end
358+
else
359+
puts "Unknown message type: #{event.type}"
360+
reply_text(event, "[UNKNOWN]\n#{event.type}")
361+
end
362+
end
363+
364+
def handle_sticker(event)
365+
# Message API available stickers
366+
# https://developers.line.me/media/messaging-api/sticker_list.pdf
367+
msgapi_available = event.message['packageId'].to_i <= 4
368+
messages = [{
369+
type: 'text',
370+
text: "[STICKER]\npackageId: #{event.message['packageId']}\nstickerId: #{event.message['stickerId']}"
371+
}]
372+
if msgapi_available
373+
messages.push(
374+
type: 'sticker',
375+
packageId: event.message['packageId'],
376+
stickerId: event.message['stickerId']
377+
)
378+
end
379+
reply_content(event, messages)
380+
end
381+
382+
def handle_location(event)
383+
message = event.message
384+
reply_content(event, {
385+
type: 'location',
386+
title: message['title'] || message['address'],
387+
address: message['address'],
388+
latitude: message['latitude'],
389+
longitude: message['longitude']
390+
})
391+
end

0 commit comments

Comments
 (0)