├── .gitattributes ├── .gitignore ├── LICENSE ├── LineBotApplication ├── LineBotApplication.sln ├── LineBotApplication │ ├── App_Start │ │ └── WebApiConfig.cs │ ├── Controllers │ │ ├── ImagesController.cs │ │ └── LineMessagesController.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── Images │ │ ├── GitHubIcon │ │ │ ├── 1040.png │ │ │ ├── 240.png │ │ │ ├── 300.png │ │ │ ├── 460.png │ │ │ └── 700.png │ │ └── richmenu.PNG │ ├── LineBotApplication.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ └── packages.config └── README.md ├── LineMessagingAPISDK ├── LineMessagingAPISDK.sln ├── LineMessagingAPISDK │ ├── LineClient.cs │ ├── LineMessagingAPISDK.csproj │ ├── Models │ │ ├── Activity.cs │ │ ├── AudioMessage.cs │ │ ├── BaseSize.cs │ │ ├── Beacon.cs │ │ ├── BeaconType.cs │ │ ├── ButtonsTemplate.cs │ │ ├── CarouselTemplate.cs │ │ ├── ConfirmTemplate.cs │ │ ├── DateTimePickerMode.cs │ │ ├── DateTimePickerTemplateAction.cs │ │ ├── Event.cs │ │ ├── EventType.cs │ │ ├── ImageCarouselTemplate.cs │ │ ├── ImageColumn.cs │ │ ├── ImageMapAction.cs │ │ ├── ImageMapActionType.cs │ │ ├── ImageMapArea.cs │ │ ├── ImageMapMessage.cs │ │ ├── ImageMessage.cs │ │ ├── LocationMessage.cs │ │ ├── Media.cs │ │ ├── MemberIdsResponse.cs │ │ ├── Message.cs │ │ ├── MessageImageMapAction.cs │ │ ├── MessageTemplateAction.cs │ │ ├── MessageType.cs │ │ ├── MulticastMessage.cs │ │ ├── Postback.cs │ │ ├── PostbackParams.cs │ │ ├── PostbackTemplateAction.cs │ │ ├── Profile.cs │ │ ├── PushMessage.cs │ │ ├── ReplyMessage.cs │ │ ├── RichMenu.cs │ │ ├── RichMenuArea.cs │ │ ├── RichMenuBounds.cs │ │ ├── RichMenuSize.cs │ │ ├── Source.cs │ │ ├── SourceType.cs │ │ ├── StickerMessage.cs │ │ ├── Template.cs │ │ ├── TemplateAction.cs │ │ ├── TemplateActionType.cs │ │ ├── TemplateColumn.cs │ │ ├── TemplateMessage.cs │ │ ├── TemplateType.cs │ │ ├── TextMessage.cs │ │ ├── URIImageMapAction.cs │ │ ├── UriTemplateAction.cs │ │ └── VideoMessage.cs │ └── Validators │ │ └── ItemCountsAttribute.cs └── README.md ├── LineWithBotFrameworkApplication ├── LineWithBotFrameworkApplication.sln ├── LineWithBotFrameworkApplication │ ├── App_Start │ │ └── WebApiConfig.cs │ ├── Controllers │ │ └── LineMessagesController.cs │ ├── Global.asax │ ├── Global.asax.cs │ ├── LineWithBotFrameworkApplication.csproj │ ├── Properties │ │ └── AssemblyInfo.cs │ ├── Services │ │ └── CacheService.cs │ ├── Web.Debug.config │ ├── Web.Release.config │ ├── Web.config │ └── packages.config └── README.md └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | bld/ 21 | [Bb]in/ 22 | [Oo]bj/ 23 | [Ll]og/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | *.VC.VC.opendb 85 | 86 | # Visual Studio profiler 87 | *.psess 88 | *.vsp 89 | *.vspx 90 | *.sap 91 | 92 | # TFS 2012 Local Workspace 93 | $tf/ 94 | 95 | # Guidance Automation Toolkit 96 | *.gpState 97 | 98 | # ReSharper is a .NET coding add-in 99 | _ReSharper*/ 100 | *.[Rr]e[Ss]harper 101 | *.DotSettings.user 102 | 103 | # JustCode is a .NET coding add-in 104 | .JustCode 105 | 106 | # TeamCity is a build add-in 107 | _TeamCity* 108 | 109 | # DotCover is a Code Coverage Tool 110 | *.dotCover 111 | 112 | # NCrunch 113 | _NCrunch_* 114 | .*crunch*.local.xml 115 | nCrunchTemp_* 116 | 117 | # MightyMoose 118 | *.mm.* 119 | AutoTest.Net/ 120 | 121 | # Web workbench (sass) 122 | .sass-cache/ 123 | 124 | # Installshield output folder 125 | [Ee]xpress/ 126 | 127 | # DocProject is a documentation generator add-in 128 | DocProject/buildhelp/ 129 | DocProject/Help/*.HxT 130 | DocProject/Help/*.HxC 131 | DocProject/Help/*.hhc 132 | DocProject/Help/*.hhk 133 | DocProject/Help/*.hhp 134 | DocProject/Help/Html2 135 | DocProject/Help/html 136 | 137 | # Click-Once directory 138 | publish/ 139 | 140 | # Publish Web Output 141 | *.[Pp]ublish.xml 142 | *.azurePubxml 143 | # TODO: Comment the next line if you want to checkin your web deploy settings 144 | # but database connection strings (with potential passwords) will be unencrypted 145 | *.pubxml 146 | *.publishproj 147 | 148 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 149 | # checkin your Azure Web App publish settings, but sensitive information contained 150 | # in these scripts will be unencrypted 151 | PublishScripts/ 152 | 153 | # NuGet Packages 154 | *.nupkg 155 | # The packages folder can be ignored because of Package Restore 156 | **/packages/* 157 | # except build/, which is used as an MSBuild target. 158 | !**/packages/build/ 159 | # Uncomment if necessary however generally it will be regenerated when needed 160 | #!**/packages/repositories.config 161 | # NuGet v3's project.json files produces more ignoreable files 162 | *.nuget.props 163 | *.nuget.targets 164 | 165 | # Microsoft Azure Build Output 166 | csx/ 167 | *.build.csdef 168 | 169 | # Microsoft Azure Emulator 170 | ecf/ 171 | rcf/ 172 | 173 | # Windows Store app package directories and files 174 | AppPackages/ 175 | BundleArtifacts/ 176 | Package.StoreAssociation.xml 177 | _pkginfo.txt 178 | 179 | # Visual Studio cache files 180 | # files ending in .cache can be ignored 181 | *.[Cc]ache 182 | # but keep track of directories ending in .cache 183 | !*.[Cc]ache/ 184 | 185 | # Others 186 | ClientBin/ 187 | ~$* 188 | *~ 189 | *.dbmdl 190 | *.dbproj.schemaview 191 | *.pfx 192 | *.publishsettings 193 | node_modules/ 194 | orleans.codegen.cs 195 | 196 | # Since there are multiple workflows, uncomment next line to ignore bower_components 197 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 198 | #bower_components/ 199 | 200 | # RIA/Silverlight projects 201 | Generated_Code/ 202 | 203 | # Backup & report files from converting an old project file 204 | # to a newer Visual Studio version. Backup files are not needed, 205 | # because we have git ;-) 206 | _UpgradeReport_Files/ 207 | Backup*/ 208 | UpgradeLog*.XML 209 | UpgradeLog*.htm 210 | 211 | # SQL Server files 212 | *.mdf 213 | *.ldf 214 | 215 | # Business Intelligence projects 216 | *.rdl.data 217 | *.bim.layout 218 | *.bim_*.settings 219 | 220 | # Microsoft Fakes 221 | FakesAssemblies/ 222 | 223 | # GhostDoc plugin setting file 224 | *.GhostDoc.xml 225 | 226 | # Node.js Tools for Visual Studio 227 | .ntvs_analysis.dat 228 | 229 | # Visual Studio 6 build log 230 | *.plg 231 | 232 | # Visual Studio 6 workspace options file 233 | *.opt 234 | 235 | # Visual Studio LightSwitch build output 236 | **/*.HTMLClient/GeneratedArtifacts 237 | **/*.DesktopClient/GeneratedArtifacts 238 | **/*.DesktopClient/ModelManifest.xml 239 | **/*.Server/GeneratedArtifacts 240 | **/*.Server/ModelManifest.xml 241 | _Pvt_Extensions 242 | 243 | # Paket dependency manager 244 | .paket/paket.exe 245 | paket-files/ 246 | 247 | # FAKE - F# Make 248 | .fake/ 249 | 250 | # JetBrains Rider 251 | .idea/ 252 | *.sln.iml 253 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Kenichiro Nakamura 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LineBotApplication", "LineBotApplication\LineBotApplication.csproj", "{0445D8B9-3F66-437A-97F0-5037DFA09A5F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {0445D8B9-3F66-437A-97F0-5037DFA09A5F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {0445D8B9-3F66-437A-97F0-5037DFA09A5F}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {0445D8B9-3F66-437A-97F0-5037DFA09A5F}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {0445D8B9-3F66-437A-97F0-5037DFA09A5F}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | 6 | namespace $safeprojectname$ 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | 14 | // Web API routes 15 | config.MapHttpAttributeRoutes(); 16 | 17 | config.Routes.MapHttpRoute( 18 | name: "ImagesController", 19 | routeTemplate: "images/{file}/{size}", 20 | defaults: new { controller = "Images", size = 1040 } 21 | ); 22 | 23 | config.Routes.MapHttpRoute( 24 | name: "DefaultApi", 25 | routeTemplate: "api/{controller}/{id}", 26 | defaults: new { id = RouteParameter.Optional } 27 | ); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Controllers/ImagesController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net; 4 | using System.Net.Http; 5 | using System.Threading.Tasks; 6 | using System.Web.Http; 7 | 8 | namespace $safeprojectname$.Controllers 9 | { 10 | public class ImagesController : ApiController 11 | { 12 | [HttpGet] 13 | public async Task Get(string file, string size) 14 | { 15 | var root = AppDomain.CurrentDomain.SetupInformation.ApplicationBase; 16 | var path = Path.Combine(root, "Images", file, size + ".png"); 17 | 18 | var response = Request.CreateResponse(HttpStatusCode.OK); 19 | response.Content = new StreamContent(new FileStream(path, FileMode.Open, FileAccess.Read)); 20 | response.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment"); 21 | response.Content.Headers.ContentDisposition.FileName = $"{size}.png"; 22 | response.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/png"); 23 | 24 | return response; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Controllers/LineMessagesController.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK; 2 | using LineMessagingAPISDK.Models; 3 | using Newtonsoft.Json; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Configuration; 7 | using System.IO; 8 | using System.Linq; 9 | using System.Net; 10 | using System.Net.Http; 11 | using System.Security.Cryptography; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | using System.Web; 15 | using System.Web.Http; 16 | 17 | namespace $safeprojectname$.Controllers 18 | { 19 | public class LineMessagesController : ApiController 20 | { 21 | /// 22 | /// POST: api/Messages 23 | /// Receive a message from a user and reply to it 24 | /// 25 | public async Task Post(HttpRequestMessage request) 26 | { 27 | if (!await VaridateSignature(request)) 28 | return Request.CreateResponse(HttpStatusCode.BadRequest); 29 | 30 | Activity activity = JsonConvert.DeserializeObject 31 | (await request.Content.ReadAsStringAsync()); 32 | 33 | // Line may send multiple events in one message, so need to handle them all. 34 | foreach (Event lineEvent in activity.Events) 35 | { 36 | LineMessageHandler handler = new LineMessageHandler(lineEvent); 37 | 38 | Profile profile = await handler.GetProfile(lineEvent.Source.UserId); 39 | 40 | switch (lineEvent.Type) 41 | { 42 | case EventType.Beacon: 43 | await handler.HandleBeaconEvent(); 44 | break; 45 | case EventType.Follow: 46 | await handler.HandleFollowEvent(); 47 | break; 48 | case EventType.Join: 49 | await handler.HandleJoinEvent(); 50 | break; 51 | case EventType.Leave: 52 | await handler.HandleLeaveEvent(); 53 | break; 54 | case EventType.Message: 55 | Message message = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 56 | switch (message.Type) 57 | { 58 | case MessageType.Text: 59 | await handler.HandleTextMessage(); 60 | break; 61 | case MessageType.Audio: 62 | case MessageType.Image: 63 | case MessageType.Video: 64 | await handler.HandleMediaMessage(); 65 | break; 66 | case MessageType.Sticker: 67 | await handler.HandleStickerMessage(); 68 | break; 69 | case MessageType.Location: 70 | await handler.HandleLocationMessage(); 71 | break; 72 | } 73 | break; 74 | case EventType.Postback: 75 | await handler.HandlePostbackEvent(); 76 | break; 77 | case EventType.Unfollow: 78 | await handler.HandleUnfollowEvent(); 79 | break; 80 | } 81 | } 82 | 83 | return Request.CreateResponse(HttpStatusCode.OK); 84 | } 85 | 86 | private async Task VaridateSignature(HttpRequestMessage request) 87 | { 88 | var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(ConfigurationManager.AppSettings["ChannelSecret"].ToString())); 89 | var computeHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(await request.Content.ReadAsStringAsync())); 90 | var contentHash = Convert.ToBase64String(computeHash); 91 | var headerHash = Request.Headers.GetValues("X-Line-Signature").First(); 92 | 93 | return contentHash == headerHash; 94 | } 95 | } 96 | 97 | public class LineMessageHandler 98 | { 99 | private Event lineEvent; 100 | private LineClient lineClient = new LineClient(ConfigurationManager.AppSettings["ChannelToken"].ToString()); 101 | 102 | public LineMessageHandler(Event lineEvent) 103 | { 104 | this.lineEvent = lineEvent; 105 | } 106 | 107 | public async Task HandleBeaconEvent() 108 | { 109 | } 110 | 111 | public async Task HandleFollowEvent() 112 | { 113 | } 114 | 115 | public async Task HandleJoinEvent() 116 | { 117 | } 118 | 119 | public async Task HandleLeaveEvent() 120 | { 121 | } 122 | 123 | public async Task HandlePostbackEvent() 124 | { 125 | string reply; 126 | // Handle DateTimePicker postback 127 | if (lineEvent.Postback?.Params != null) 128 | { 129 | var dateTime = lineEvent.Postback?.Params; 130 | reply = $"DateTime: {dateTime.DateTime}, Date: {dateTime.Date}, Time: {dateTime.Time}"; 131 | } 132 | else 133 | { 134 | reply = lineEvent.Postback.Data; 135 | } 136 | await Reply(new TextMessage(reply)); 137 | } 138 | 139 | public async Task HandleUnfollowEvent() 140 | { 141 | } 142 | 143 | public async Task GetProfile(string mid) 144 | { 145 | return await lineClient.GetProfile(mid); 146 | } 147 | 148 | public async Task HandleTextMessage() 149 | { 150 | var textMessage = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 151 | Message replyMessage = null; 152 | if (textMessage.Text.ToLower() == "buttons") 153 | { 154 | List actions = new List(); 155 | actions.Add(new MessageTemplateAction("Message Label", "sample data")); 156 | actions.Add(new PostbackTemplateAction("Postback Label", "sample data", "sample data")); 157 | actions.Add(new UriTemplateAction("Uri Label", "https://github.com/kenakamu")); 158 | ButtonsTemplate buttonsTemplate = new ButtonsTemplate("https://github.com/apple-touch-icon.png", "Sample Title", "Sample Text", actions); 159 | 160 | replyMessage = new TemplateMessage("Buttons", buttonsTemplate); 161 | } 162 | else if (textMessage.Text.ToLower() == "confirm") 163 | { 164 | List actions = new List(); 165 | actions.Add(new MessageTemplateAction("Yes", "yes")); 166 | actions.Add(new MessageTemplateAction("No", "no")); 167 | ConfirmTemplate confirmTemplate = new ConfirmTemplate("Confirm Test", actions); 168 | replyMessage = new TemplateMessage("Confirm", confirmTemplate); 169 | } 170 | else if (textMessage.Text.ToLower() == "carousel") 171 | { 172 | List columns = new List(); 173 | List actions1 = new List(); 174 | List actions2 = new List(); 175 | 176 | // Add actions. 177 | actions1.Add(new MessageTemplateAction("Message Label", "sample data")); 178 | actions1.Add(new PostbackTemplateAction("Postback Label", "sample data", "sample data")); 179 | actions1.Add(new UriTemplateAction("Uri Label", "https://github.com/kenakamu")); 180 | 181 | // Add datetime picker actions 182 | actions2.Add(new DatetimePickerTemplateAction("DateTime Picker", "DateTime", DatetimePickerMode.Datetime, "2017-07-21T13:00")); 183 | actions2.Add(new DatetimePickerTemplateAction("Date Picker", "Date", DatetimePickerMode.Date, "2017-07-21")); 184 | actions2.Add(new DatetimePickerTemplateAction("Time Picker", "Time", DatetimePickerMode.Time, "13:00")); 185 | 186 | columns.Add(new TemplateColumn() { Title = "Casousel 1 Title", Text = "Casousel 1 Text", ThumbnailImageUrl = "https://github.com/apple-touch-icon.png", Actions = actions1 }); 187 | columns.Add(new TemplateColumn() { Title = "Casousel 2 Title", Text = "Casousel 2 Text", ThumbnailImageUrl = "https://github.com/apple-touch-icon.png", Actions = actions2 }); 188 | CarouselTemplate carouselTemplate = new CarouselTemplate(columns); 189 | replyMessage = new TemplateMessage("Carousel", carouselTemplate); 190 | } 191 | else if (textMessage.Text.ToLower() == "imagecarousel") 192 | { 193 | List columns = new List(); 194 | UriTemplateAction action = new UriTemplateAction("Uri Label", "https://github.com/kenakamu"); 195 | 196 | columns.Add(new ImageColumn("https://github.com/apple-touch-icon.png", action)); 197 | columns.Add(new ImageColumn("https://github.com/apple-touch-icon.png", action)); 198 | columns.Add(new ImageColumn("https://github.com/apple-touch-icon.png", action)); 199 | columns.Add(new ImageColumn("https://github.com/apple-touch-icon.png", action)); 200 | columns.Add(new ImageColumn("https://github.com/apple-touch-icon.png", action)); 201 | 202 | ImageCarouselTemplate carouselTemplate = new ImageCarouselTemplate(columns); 203 | 204 | replyMessage = new TemplateMessage("Carousel", carouselTemplate); 205 | } 206 | else if (textMessage.Text.ToLower() == "imagemap") 207 | { 208 | var url = HttpContext.Current.Request.Url; 209 | var imageUrl = $"{url.Scheme}://{url.Host}:{url.Port}/images/githubicon"; 210 | List actions = new List(); 211 | actions.Add(new UriImageMapAction("http://github.com", new ImageMapArea(0, 0, 520, 1040))); 212 | actions.Add(new MessageImageMapAction("I love LINE!", new ImageMapArea(520, 0, 520, 1040))); 213 | replyMessage = new ImageMapMessage(imageUrl, "GitHub", new BaseSize(1040, 1040), actions); 214 | } 215 | else if (textMessage.Text.ToLower() == "addrichmenu") 216 | { 217 | // Create Rich Menu 218 | RichMenu richMenu = new RichMenu() 219 | { 220 | Size = new RichMenuSize(1686), 221 | Selected = false, 222 | Name = "nice richmenu", 223 | ChatBarText = "touch me", 224 | Areas = new List() 225 | { 226 | new RichMenuArea() 227 | { 228 | Action = new PostbackTemplateAction("action=buy&itemid=123"), 229 | Bounds = new RichMenuBounds(0, 0, 2500, 1686) 230 | } 231 | } 232 | }; 233 | 234 | 235 | var richMenuId = await lineClient.CreateRichMenu(richMenu); 236 | var image = new MemoryStream(File.ReadAllBytes(HttpContext.Current.Server.MapPath(@"~\Images\richmenu.PNG"))); 237 | // Upload Image 238 | await lineClient.UploadRichMenuImage(richMenuId, image); 239 | // Link to user 240 | await lineClient.LinkRichMenuToUser(lineEvent.Source.UserId, richMenuId); 241 | } 242 | else if (textMessage.Text.ToLower() == "deleterichmenu") 243 | { 244 | // Get Rich Menu for the user 245 | var richMenuId = await lineClient.GetRichMenuIdForUser(lineEvent.Source.UserId); 246 | 247 | await lineClient.UnlinkRichMenuToUser(lineEvent.Source.UserId); 248 | await lineClient.DeleteRichMenu(richMenuId); 249 | } 250 | else if (textMessage.Text.ToLower() == "deleteallrichmenu") 251 | { 252 | // Get Rich Menu for the user 253 | var richMenuList = await lineClient.GetRichMenuList(); 254 | foreach (var richMenu in richMenuList) 255 | { 256 | await lineClient.DeleteRichMenu(richMenu["richMenuId"].ToString()); 257 | } 258 | } 259 | else 260 | { 261 | replyMessage = new TextMessage(textMessage.Text); 262 | } 263 | await Reply(replyMessage); 264 | } 265 | 266 | public async Task HandleMediaMessage() 267 | { 268 | Message message = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 269 | // Get media from Line server. 270 | Media media = await lineClient.GetContent(message.Id); 271 | Message replyMessage = null; 272 | 273 | // Reply Image 274 | switch (message.Type) 275 | { 276 | case MessageType.Image: 277 | case MessageType.Video: 278 | case MessageType.Audio: 279 | replyMessage = new ImageMessage("https://github.com/apple-touch-icon.png", "https://github.com/apple-touch-icon.png"); 280 | break; 281 | } 282 | 283 | await Reply(replyMessage); 284 | } 285 | 286 | public async Task HandleStickerMessage() 287 | { 288 | //https://devdocs.line.me/files/sticker_list.pdf 289 | var stickerMessage = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 290 | var replyMessage = new StickerMessage("1", "1"); 291 | await Reply(replyMessage); 292 | } 293 | 294 | public async Task HandleLocationMessage() 295 | { 296 | var locationMessage = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 297 | LocationMessage replyMessage = new LocationMessage( 298 | locationMessage.Title, 299 | locationMessage.Address, 300 | locationMessage.Latitude, 301 | locationMessage.Longitude); 302 | await Reply(replyMessage); 303 | } 304 | 305 | private async Task Reply(Message replyMessage) 306 | { 307 | try 308 | { 309 | await lineClient.ReplyToActivityAsync(lineEvent.CreateReply(message: replyMessage)); 310 | } 311 | catch 312 | { 313 | await lineClient.PushAsync(lineEvent.CreatePush(message: replyMessage)); 314 | } 315 | } 316 | } 317 | } 318 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="$safeprojectname$.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Http; 6 | using System.Web.Routing; 7 | 8 | namespace $safeprojectname$ 9 | { 10 | public class WebApiApplication : System.Web.HttpApplication 11 | { 12 | protected void Application_Start() 13 | { 14 | GlobalConfiguration.Configure(WebApiConfig.Register); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Images/GitHubIcon/1040.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenakamu/line-bot-sdk-csharp/b94b28fadf13a56d4f34aeafdcb3b2e7482120e0/LineBotApplication/LineBotApplication/Images/GitHubIcon/1040.png -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Images/GitHubIcon/240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenakamu/line-bot-sdk-csharp/b94b28fadf13a56d4f34aeafdcb3b2e7482120e0/LineBotApplication/LineBotApplication/Images/GitHubIcon/240.png -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Images/GitHubIcon/300.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenakamu/line-bot-sdk-csharp/b94b28fadf13a56d4f34aeafdcb3b2e7482120e0/LineBotApplication/LineBotApplication/Images/GitHubIcon/300.png -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Images/GitHubIcon/460.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenakamu/line-bot-sdk-csharp/b94b28fadf13a56d4f34aeafdcb3b2e7482120e0/LineBotApplication/LineBotApplication/Images/GitHubIcon/460.png -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Images/GitHubIcon/700.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenakamu/line-bot-sdk-csharp/b94b28fadf13a56d4f34aeafdcb3b2e7482120e0/LineBotApplication/LineBotApplication/Images/GitHubIcon/700.png -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Images/richmenu.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kenakamu/line-bot-sdk-csharp/b94b28fadf13a56d4f34aeafdcb3b2e7482120e0/LineBotApplication/LineBotApplication/Images/richmenu.PNG -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/LineBotApplication.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | 10 | 11 | 2.0 12 | {0445D8B9-3F66-437A-97F0-5037DFA09A5F} 13 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 14 | Library 15 | Properties 16 | LineBotApplication 17 | LineBotApplication 18 | v4.6.2 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | full 33 | false 34 | bin\ 35 | DEBUG;TRACE 36 | prompt 37 | 4 38 | 39 | 40 | pdbonly 41 | true 42 | bin\ 43 | TRACE 44 | prompt 45 | 4 46 | 47 | 48 | 49 | ..\packages\LineMessagingAPI.CSharp.1.6.0\lib\LineMessagingAPISDK.dll 50 | 51 | 52 | ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.0\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll 53 | True 54 | 55 | 56 | 57 | ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll 58 | 59 | 60 | ..\packages\System.ComponentModel.Annotations.4.4.0\lib\net461\System.ComponentModel.Annotations.dll 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 82 | 83 | 84 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 85 | 86 | 87 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | PreserveNewest 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | Global.asax 108 | 109 | 110 | 111 | 112 | 113 | Designer 114 | 115 | 116 | Web.config 117 | 118 | 119 | Web.config 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 10.0 128 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | True 138 | True 139 | 1590 140 | / 141 | http://localhost:1590/ 142 | False 143 | False 144 | 145 | 146 | False 147 | 148 | 149 | 150 | 151 | 152 | 153 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 154 | 155 | 156 | 157 | 158 | 165 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("$safeprojectname$")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("$safeprojectname$")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0445d8b9-3f66-437a-97f0-5037dfa09a5f")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /LineBotApplication/LineBotApplication/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /LineBotApplication/README.md: -------------------------------------------------------------------------------- 1 | # LineBotApplition 2 | Visual Studio 2015 project template for Line Messaging API. 3 | 4 | About the LINE Messaging API 5 | ------------------------ 6 | 7 | See the official API documentation for more information. 8 | 9 | English: https://devdocs.line.me/en/
10 | Japanese: https://devdocs.line.me/ja/ 11 | 12 | ## Install 13 | You can download from https://github.com/kenakamu/line-bot-sdk-csharp/releases 14 | 15 | 1. Download Line Bot Application.zip from [here](https://github.com/kenakamu/line-bot-sdk-csharp/releases) and 16 | save the zip file to your Visual Studio 2015 templates directory which is traditionally in 17 | "%USERPROFILE%\Documents\Visual Studio 2015\Templates\ProjectTemplates\Visual C#\" 18 | 2. Open Visual Studio 2015 19 | 3. Create a new C# project using the new Line Bot Application template. 20 | 4. Update ChannelSecret and ChannelToken in Web.Config file.
21 | You can get there values from https://business.line.me 22 | 23 | # Template features 24 | This templates does followings. 25 | 26 | ### Varidate Signature 27 | When receiving Post message from Line Platform, it verifies the request by following the documentation at https://devdocs.line.me/en/#signature-validation 28 | 29 | ### Parse and handle Incoming message 30 | Parse the incoming Post body and handle event. LineMessageHandler class contains methods to handle each type. 31 | 32 | ### Create reply message sample 33 | Several types such as Text, Location or Media type of message already has sample reply implemented. 34 | 35 | ### Reply/Push message 36 | Finally, it replies to Line Platform by using either reply or push. Find detail information here. 37 | 38 | Reply Message: https://devdocs.line.me/en/#reply-message
39 | Push Message: https://devdocs.line.me/en/#push-message 40 | 41 | ### 日本語の記事 ### 42 | 以下記事に使い方の例が非常にわかりやすく掲載されていますので、是非ご覧ください。 43 | 44 | LUISを使って頭の悪いLINE Botを作ってみよう! 45 | http://www.atmarkit.co.jp/ait/articles/1702/03/news026.html 46 | 47 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LineMessagingAPISDK", "LineMessagingAPISDK\LineMessagingAPISDK.csproj", "{F01AA9DB-E769-4F6A-902A-373C5CA34341}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {F01AA9DB-E769-4F6A-902A-373C5CA34341}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {F01AA9DB-E769-4F6A-902A-373C5CA34341}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {F01AA9DB-E769-4F6A-902A-373C5CA34341}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {F01AA9DB-E769-4F6A-902A-373C5CA34341}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/LineClient.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK.Models; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Converters; 4 | using Newtonsoft.Json.Linq; 5 | using Newtonsoft.Json.Serialization; 6 | using System; 7 | using System.Collections.Generic; 8 | using System.IO; 9 | using System.Linq; 10 | using System.Net.Http; 11 | using System.Net.Http.Headers; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | 15 | namespace LineMessagingAPISDK 16 | { 17 | public class LineClient 18 | { 19 | const string replyEndpoint = "https://api.line.me/v2/bot/message/reply"; 20 | const string pushEndpoint = "https://api.line.me/v2/bot/message/push"; 21 | const string multicastEndpoint = "https://api.line.me/v2/bot/message/multicast"; 22 | const string contentEndpoint = "https://api.line.me/v2/bot/message/{0}/content"; 23 | const string profileEndpoint = "https://api.line.me/v2/bot/profile/{0}"; 24 | const string leaveEndpoint = "https://api.line.me/v2/bot/room/{0}/leave"; 25 | const string groupMemberProfileEndpoint = "https://api.line.me/v2/bot/group/{0}/member/{1}"; 26 | const string roomMemberProfileEndpoint = "https://api.line.me/v2/bot/room/{0}/member/{1}"; 27 | const string groupMemberIdsEndpoint = "https://api.line.me/v2/bot/group/{0}/members/ids?start={1}"; 28 | const string roomMemberIdsEndpoint = "https://api.line.me/v2/bot/room/{0}/members/ids?start={1}"; 29 | const string richMenuEndpoint = "https://api.line.me/v2/bot/richmenu"; 30 | const string richMenuImageEndpoint = "https://api.line.me/v2/bot/richmenu/{0}/content"; 31 | const string richMenuIdForUserEndpoint = "https://api.line.me/v2/bot/user/{0}/richmenu"; 32 | 33 | static private HttpClient client; 34 | static private JsonSerializerSettings settings; 35 | 36 | public LineClient(string accessToken) 37 | { 38 | if (client == null) 39 | { 40 | client = new HttpClient(); 41 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); 42 | } 43 | 44 | if (settings == null) 45 | { 46 | settings = new JsonSerializerSettings() 47 | { 48 | ContractResolver = new CamelCasePropertyNamesContractResolver(), 49 | NullValueHandling = NullValueHandling.Ignore 50 | }; 51 | 52 | settings.Converters.Add(new StringEnumConverter(true)); 53 | } 54 | } 55 | 56 | /// 57 | /// https://devdocs.line.me/en/#reply-message 58 | /// Respond to events from users, groups, and rooms. 59 | /// Webhooks are used to notify you when an event occurs.For events that you can respond to, a replyToken is issued for replying to messages. 60 | /// Because the replyToken becomes invalid after a certain period of time, responses should be sent as soon as a message is received.Reply tokens can only be used once. 61 | /// 62 | /// 63 | public async Task ReplyToActivityAsync(ReplyMessage replyMessage) 64 | { 65 | StringContent content = new StringContent( 66 | JsonConvert.SerializeObject(replyMessage, settings), 67 | Encoding.UTF8, "application/json"); 68 | var result = await client.PostAsync(replyEndpoint, content); 69 | 70 | if (result.IsSuccessStatusCode) 71 | return; 72 | else 73 | throw new Exception(await result.Content.ReadAsStringAsync()); 74 | //200 OK Request successful 75 | //400 Bad Request Problem with the request 76 | //401 Unauthorized Valid Channel access token is not specified 77 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 78 | //429 Too Many Requests Exceeded the rate limit for API calls 79 | //500 Internal Server Error Error on the internal server 80 | } 81 | 82 | /// 83 | /// https://devdocs.line.me/en/#push-message 84 | /// Send messages to a user, group, or room at any time. 85 | /// INFO Use of the Push Message API is limited to certain plans. 86 | /// 87 | /// 88 | public async Task PushAsync(PushMessage pushMessage) 89 | { 90 | StringContent content = new StringContent( 91 | JsonConvert.SerializeObject(pushMessage, settings), 92 | Encoding.UTF8, "application/json"); 93 | var result = await client.PostAsync(pushEndpoint, content); 94 | if (result.IsSuccessStatusCode) 95 | return; 96 | else 97 | throw new Exception(await result.Content.ReadAsStringAsync()); 98 | //200 OK Request successful 99 | //400 Bad Request Problem with the request 100 | //401 Unauthorized Valid Channel access token is not specified 101 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 102 | //429 Too Many Requests Exceeded the rate limit for API calls 103 | //500 Internal Server Error Error on the internal server 104 | } 105 | 106 | /// 107 | /// https://devdocs.line.me/en/#multicast 108 | /// Send messages to multiple users at any time 109 | /// 110 | /// IDs of the receivers. Max: 150 users. Use IDs returned via the webhook event of source users. IDs of groups or rooms cannot be used. Do not use the LINE ID found on the LINE app. 111 | /// Messages Max: 5 112 | public async Task MulticastAsync(List to, List messages) 113 | { 114 | if (to.Count > 150) 115 | throw new Exception("Max: 150 users"); 116 | if (messages.Count > 5) 117 | throw new Exception("Max: 5 Messages"); 118 | 119 | MulticastMessage message = new MulticastMessage() 120 | { 121 | To = to, 122 | Messages = messages 123 | }; 124 | 125 | StringContent content = new StringContent( 126 | JsonConvert.SerializeObject(message, settings), 127 | Encoding.UTF8, "application/json"); 128 | var result = await client.PostAsync(multicastEndpoint, content); 129 | if (result.IsSuccessStatusCode) 130 | return; 131 | else 132 | throw new Exception(await result.Content.ReadAsStringAsync()); 133 | //200 OK Request successful 134 | //400 Bad Request Problem with the request 135 | //401 Unauthorized Valid Channel access token is not specified 136 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 137 | //429 Too Many Requests Exceeded the rate limit for API calls 138 | //500 Internal Server Error Error on the internal server 139 | } 140 | 141 | /// 142 | /// https://devdocs.line.me/en/#content 143 | /// Retrieve image, video, and audio data sent by users. 144 | /// 145 | /// 146 | /// 147 | public async Task GetContent(string messageId) 148 | { 149 | var result = await client.GetAsync(string.Format(contentEndpoint, messageId)); 150 | if (result.IsSuccessStatusCode) 151 | { 152 | return new Media() 153 | { 154 | Content = await result.Content.ReadAsStreamAsync(), 155 | ContentType = result.Content.Headers.ContentType.MediaType, 156 | FileName = result.Content.Headers.ContentDisposition != null ? 157 | result.Content.Headers.ContentDisposition.FileName.Replace("\"", "") : 158 | Guid.NewGuid().ToString() 159 | }; 160 | } 161 | else 162 | return null; 163 | } 164 | 165 | /// 166 | /// https://devdocs.line.me/en/#bot-api-get-profile 167 | /// Get user profile information. 168 | /// 169 | /// User MID 170 | /// 171 | public async Task GetProfile(string mid) 172 | { 173 | var result = await client.GetAsync(string.Format(profileEndpoint, mid)); 174 | if (!result.IsSuccessStatusCode) 175 | return null; 176 | 177 | Profile profiles = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); 178 | 179 | return profiles; 180 | } 181 | 182 | /// 183 | /// https://devdocs.line.me/en/#leave 184 | /// Leave a group or room 185 | /// 186 | /// group id or room id 187 | public async Task Leave(string id) 188 | { 189 | var result = await client.GetAsync(string.Format(leaveEndpoint, id)); 190 | if (result.IsSuccessStatusCode) 191 | return; 192 | else 193 | throw new Exception(await result.Content.ReadAsStringAsync()); 194 | //200 OK Request successful 195 | //400 Bad Request Problem with the request 196 | //401 Unauthorized Valid Channel access token is not specified 197 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 198 | //429 Too Many Requests Exceeded the rate limit for API calls 199 | //500 Internal Server Error Error on the internal server 200 | } 201 | 202 | /// 203 | /// https://developers.line.me/en/docs/messaging-api/reference/#get-group-member-profile 204 | /// Get group member profile 205 | /// 206 | /// group id 207 | /// member id 208 | /// 209 | public async Task GetGroupMemberProfile(string groupId, string memberId) 210 | { 211 | var result = await client.GetAsync(string.Format(groupMemberProfileEndpoint, groupId, memberId)); 212 | if (!result.IsSuccessStatusCode) 213 | return null; 214 | else 215 | { 216 | Profile profile = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); 217 | return profile; 218 | } 219 | } 220 | 221 | /// 222 | /// https://developers.line.me/en/docs/messaging-api/reference/#get-room-member-profile 223 | /// Get room member profile 224 | /// 225 | /// room id 226 | /// member id 227 | /// 228 | public async Task GetRoomMemberProfile(string roomId, string memberId) 229 | { 230 | var result = await client.GetAsync(string.Format(roomMemberProfileEndpoint, roomId, memberId)); 231 | if (!result.IsSuccessStatusCode) 232 | return null; 233 | else 234 | { 235 | Profile profile = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); 236 | return profile; 237 | } 238 | } 239 | 240 | /// 241 | /// https://developers.line.me/en/docs/messaging-api/reference/#get-group-member-user-ids 242 | /// Get group member user IDs 243 | /// This feature is only available for LINE@ Approved accounts or official accounts. 244 | /// 245 | /// group id 246 | /// continuationToken 247 | /// 248 | public async Task GetGroupMemberIds(string groupId, string start = "") 249 | { 250 | var result = await client.GetAsync(string.Format(groupMemberIdsEndpoint, groupId, start)); 251 | if (!result.IsSuccessStatusCode) 252 | return null; 253 | else 254 | { 255 | var groupMembersResponse = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); 256 | return groupMembersResponse; 257 | } 258 | } 259 | 260 | /// 261 | /// https://developers.line.me/en/docs/messaging-api/reference/#get-room-member-user-ids 262 | /// Get room member user IDs 263 | /// This feature is only available for LINE@ Approved accounts or official accounts. 264 | /// 265 | /// room id 266 | /// continuationToken 267 | /// 268 | public async Task GetRoomMemberIds(string roomId, string start = "") 269 | { 270 | var result = await client.GetAsync(string.Format(roomMemberIdsEndpoint, roomId, start)); 271 | if (!result.IsSuccessStatusCode) 272 | return null; 273 | else 274 | { 275 | var groupMembersResponse = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); 276 | return groupMembersResponse; 277 | } 278 | } 279 | 280 | /// 281 | /// Gets a rich menu via a rich menu ID. 282 | /// https://developers.line.me/en/docs/messaging-api/reference/#get-rich-menu 283 | /// 284 | /// RichMenu Id 285 | /// 286 | public async Task GetRichMenu(string richMenuId) 287 | { 288 | var result = await client.GetAsync(richMenuEndpoint + $"/{richMenuId}"); 289 | if (!result.IsSuccessStatusCode) 290 | return null; 291 | else 292 | { 293 | var richMenu = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); 294 | return richMenu; 295 | } 296 | } 297 | 298 | /// 299 | /// Creates a rich menu. 300 | /// https://developers.line.me/en/docs/messaging-api/reference/#create-rich-menu 301 | /// Note: You must upload a rich menu image and link the rich menu to a user for the rich menu to be displayed.You can create up to 10 rich menus for one bot. 302 | /// 303 | /// RichMenu object 304 | /// richMenuId 305 | public async Task CreateRichMenu(RichMenu richMenu) 306 | { 307 | 308 | 309 | StringContent content = new StringContent( 310 | JsonConvert.SerializeObject(richMenu, settings), 311 | Encoding.UTF8, "application/json"); 312 | var result = await client.PostAsync(richMenuEndpoint, content); 313 | if (result.IsSuccessStatusCode) 314 | return JObject.Parse(await result.Content.ReadAsStringAsync())["richMenuId"].ToString(); 315 | else 316 | throw new Exception(await result.Content.ReadAsStringAsync()); 317 | //200 OK Request successful 318 | //400 Bad Request Problem with the request 319 | //401 Unauthorized Valid Channel access token is not specified 320 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 321 | //429 Too Many Requests Exceeded the rate limit for API calls 322 | //500 Internal Server Error Error on the internal server 323 | } 324 | 325 | /// 326 | /// Deletes a rich menu. 327 | /// https://developers.line.me/en/docs/messaging-api/reference/#delete-rich-menu 328 | /// 329 | /// RichMenu Id 330 | /// 331 | public async Task DeleteRichMenu(string richMenuId) 332 | { 333 | var result = await client.DeleteAsync(richMenuEndpoint + $"/{richMenuId}"); 334 | if (result.IsSuccessStatusCode) 335 | return; 336 | else 337 | throw new Exception(await result.Content.ReadAsStringAsync()); 338 | //200 OK Request successful 339 | //400 Bad Request Problem with the request 340 | //401 Unauthorized Valid Channel access token is not specified 341 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 342 | //429 Too Many Requests Exceeded the rate limit for API calls 343 | //500 Internal Server Error Error on the internal server 344 | } 345 | 346 | /// 347 | /// Gets the ID of the rich menu linked to a user. 348 | /// https://developers.line.me/en/docs/messaging-api/reference/#get-rich-menu-id-of-user 349 | /// 350 | /// RichMenu Id 351 | /// 352 | public async Task GetRichMenuIdForUser(string userId) 353 | { 354 | var result = await client.GetAsync(string.Format(richMenuIdForUserEndpoint, userId)); 355 | if (!result.IsSuccessStatusCode) 356 | return null; 357 | else 358 | { 359 | var richMenu = JsonConvert.DeserializeObject(await result.Content.ReadAsStringAsync()); 360 | return richMenu.RichMenuId; 361 | } 362 | } 363 | 364 | /// 365 | /// Links a rich menu to a user. 366 | /// https://developers.line.me/en/docs/messaging-api/reference/#link-rich-menu-to-user 367 | /// Note: Only one rich menu can be linked to a user at one time 368 | /// 369 | /// User Id 370 | /// RichMenu Id 371 | /// 372 | public async Task LinkRichMenuToUser(string userId, string richMenuId) 373 | { 374 | var result = await client.PostAsync(string.Format(richMenuIdForUserEndpoint, userId) + $"/{richMenuId}", null); 375 | if (result.IsSuccessStatusCode) 376 | return; 377 | else 378 | throw new Exception(await result.Content.ReadAsStringAsync()); 379 | //200 OK Request successful 380 | //400 Bad Request Problem with the request 381 | //401 Unauthorized Valid Channel access token is not specified 382 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 383 | //429 Too Many Requests Exceeded the rate limit for API calls 384 | //500 Internal Server Error Error on the internal server 385 | } 386 | 387 | /// 388 | /// Unlinks a rich menu from a user. 389 | /// https://developers.line.me/en/docs/messaging-api/reference/#unlink-rich-menu-from-user 390 | /// 391 | /// User Id 392 | /// 393 | public async Task UnlinkRichMenuToUser(string userId) 394 | { 395 | var result = await client.DeleteAsync(string.Format(richMenuIdForUserEndpoint, userId)); 396 | if (result.IsSuccessStatusCode) 397 | return; 398 | else 399 | throw new Exception(await result.Content.ReadAsStringAsync()); 400 | //200 OK Request successful 401 | //400 Bad Request Problem with the request 402 | //401 Unauthorized Valid Channel access token is not specified 403 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 404 | //429 Too Many Requests Exceeded the rate limit for API calls 405 | //500 Internal Server Error Error on the internal server 406 | } 407 | 408 | /// 409 | /// Downloads an image associated with a rich menu. 410 | /// https://developers.line.me/en/docs/messaging-api/reference/#download-rich-menu-image 411 | /// 412 | /// RichMenuId 413 | /// 414 | public async Task GetRichMenuImage(string richMenuId) 415 | { 416 | var result = await client.DeleteAsync(string.Format(richMenuImageEndpoint, richMenuId)); 417 | if (!result.IsSuccessStatusCode) 418 | return null; 419 | 420 | return new Media() 421 | { 422 | Content = await result.Content.ReadAsStreamAsync(), 423 | ContentType = result.Content.Headers.ContentType.MediaType, 424 | FileName = result.Content.Headers.ContentDisposition != null ? 425 | result.Content.Headers.ContentDisposition.FileName.Replace("\"", "") : 426 | Guid.NewGuid().ToString() 427 | }; 428 | } 429 | 430 | /// 431 | /// Upload rich menu image 432 | /// https://developers.line.me/en/docs/messaging-api/reference/#upload-rich-menu-image 433 | /// Notes: 434 | /// Images must have one of the following resolutions: 2500x1686, 2500x843. 435 | /// You cannot replace an image attached to a rich menu.To update your rich menu image, create a new rich menu object and upload another image. 436 | /// 437 | /// RichMenu Id 438 | /// 439 | public async Task UploadRichMenuImage(string richMenuId, Stream image) 440 | { 441 | //client.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "image/png"); 442 | var streamContent = new StreamContent(image); 443 | streamContent.Headers.ContentType = new MediaTypeHeaderValue("image/png"); 444 | var result = await client.PostAsync(string.Format(richMenuImageEndpoint, richMenuId), streamContent); 445 | if (result.IsSuccessStatusCode) 446 | return; 447 | else 448 | throw new Exception(await result.Content.ReadAsStringAsync()); 449 | //200 OK Request successful 450 | //400 Bad Request Problem with the request 451 | //401 Unauthorized Valid Channel access token is not specified 452 | //403 Forbidden Not authorized to use the API.Confirm that your account or plan is authorized to used the API. 453 | //429 Too Many Requests Exceeded the rate limit for API calls 454 | //500 Internal Server Error Error on the internal server 455 | } 456 | 457 | /// 458 | /// Gets a list of all uploaded rich menus. 459 | /// https://developers.line.me/en/docs/messaging-api/reference/#get-rich-menu-list 460 | /// 461 | /// JArray which contains RichMenu data as JSON 462 | public async Task GetRichMenuList() 463 | { 464 | var result = await client.GetAsync(richMenuEndpoint + "/list"); 465 | if (!result.IsSuccessStatusCode) 466 | return null; 467 | else 468 | { 469 | // Action has several types and it inherits from abstract class, thus it is not possible to simply deserialize into RichMenu. 470 | // If you want to deserize it into actual type, please do so by yourself from the returned result and i keep this as JArray for now. 471 | var richMenus = (JToken.Parse(await result.Content.ReadAsStringAsync())["richmenus"]) as JArray; 472 | return richMenus; 473 | } 474 | } 475 | } 476 | } 477 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/LineMessagingAPISDK.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Activity.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | public class Activity 7 | { 8 | [JsonProperty("events")] 9 | public Event[] Events { get; set; } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/AudioMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | public class AudioMessage : Message 7 | { 8 | /// 9 | /// URL of audio file (Max: 1000 characters) 10 | /// HTTPS 11 | /// m4a 12 | /// Less than 1 minute 13 | /// Max 10 MB 14 | /// 15 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 16 | [RegularExpression("^https://.*(m4a)$", ErrorMessage = "Require HTTPS and m4a")] 17 | [JsonProperty("originalContentUrl")] 18 | public string OriginalContentUrl { get; set; } 19 | 20 | [JsonProperty("duration")] 21 | public int? DurationInMilliseconds { get; set; } 22 | 23 | public AudioMessage(string originalContentUrl, int durationInMilliseconds) 24 | { 25 | Type = MessageType.Audio; 26 | this.OriginalContentUrl = originalContentUrl; 27 | this.DurationInMilliseconds = durationInMilliseconds; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/BaseSize.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | /// 6 | /// BaseSize property of ImageMap 7 | /// 8 | public class BaseSize 9 | { 10 | /// 11 | /// Width of base image (set to 1040px) 12 | /// 13 | [JsonProperty("width")] 14 | public double Width { get; set; } 15 | 16 | /// 17 | /// Height of base image(set to the height that corresponds to a width of 1040px) 18 | /// 19 | [JsonProperty("height")] 20 | public double Height { get; set; } 21 | 22 | public BaseSize(double width, double height) 23 | { 24 | this.Width = width; 25 | this.Height = height; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Beacon.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | /// 6 | /// Event object for when a user enters or leaves the range of a LINE Beacon. You can reply to beacon events. 7 | /// 8 | public class Beacon 9 | { 10 | /// 11 | /// Hardware ID of the beacon that was detected 12 | /// 13 | [JsonProperty("hwid")] 14 | public string Hwid { get; set; } 15 | 16 | /// 17 | /// Type of beacon event 18 | /// 19 | [JsonProperty("type")] 20 | public BeaconType Type { get; set; } 21 | 22 | /// 23 | /// Optional. Device message of beacon that was detected 24 | /// 25 | [JsonProperty("dm")] 26 | public string Dm { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/BeaconType.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum BeaconType { Enter, Leave, Banner } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ButtonsTemplate.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK.Validators; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.DataAnnotations; 6 | 7 | namespace LineMessagingAPISDK.Models 8 | { 9 | /// 10 | /// Template message with an image, title, text, and multiple action buttons. 11 | /// 12 | public class ButtonsTemplate : Template 13 | { 14 | /// 15 | /// Image URL (Max: 1000 characters) 16 | /// HTTPS 17 | /// JPEG or PNG 18 | /// Aspect ratio: 1:1.51 19 | /// Max width: 1024px 20 | /// Max: 1 MB 21 | /// 22 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 23 | [RegularExpression("^https://.*(jpg|jpeg|png)$", ErrorMessage = "Require HTTPS and jpeg/png")] 24 | [JsonProperty("thumbnailImageUrl")] 25 | public string ThumbnailImageUrl { get; set; } 26 | 27 | private string title; 28 | /// 29 | /// Max: 40 characters 30 | /// text will be truncated if it exceeds 40 characters. 31 | /// 32 | [JsonProperty("title")] 33 | public string Title 34 | { 35 | get { return title; } 36 | set { title = value?.Length > 40 ? value.Substring(0, 40) : value; } 37 | } 38 | 39 | private string text; 40 | /// 41 | /// Message text 42 | /// Max: 160 characters(no image or title) 43 | /// Max: 60 characters(message with an image or title) 44 | /// text will be truncated if it exceeds its limit. 45 | /// 46 | [JsonProperty("text")] 47 | public string Text 48 | { 49 | get { return text; } 50 | set 51 | { 52 | if (string.IsNullOrEmpty(ThumbnailImageUrl) && string.IsNullOrEmpty(Title)) 53 | text = value?.Length > 160 ? value.Substring(0, 160) : value; 54 | else 55 | text = value?.Length > 60 ? value.Substring(0, 60) : value; 56 | } 57 | } 58 | 59 | /// 60 | /// Action when tapped 61 | /// Max: 4 62 | /// 63 | [ItemCounts(4, ErrorMessage ="You can store up to 4 actions")] 64 | [JsonProperty("actions")] 65 | public List Actions { get; set; } 66 | 67 | public ButtonsTemplate(string thumbnailImageUrl= null, string title = null, string text = null, List actions = null) 68 | { 69 | Type = TemplateType.Buttons; 70 | this.ThumbnailImageUrl = thumbnailImageUrl; 71 | this.Title = title; 72 | this.Text = text; 73 | this.Actions = actions ?? new List(); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/CarouselTemplate.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK.Validators; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace LineMessagingAPISDK.Models 7 | { 8 | /// 9 | /// Template message with multiple columns which can be cycled like a carousel. 10 | /// 11 | public class CarouselTemplate : Template 12 | { 13 | /// 14 | /// Array of columns 15 | /// Max: 10 16 | /// 17 | [ItemCounts(10, ErrorMessage = "You can store up to 10 Columns")] 18 | [JsonProperty("columns")] 19 | public List Columns { get; set; } 20 | 21 | public CarouselTemplate(List columns = null) 22 | { 23 | Type = TemplateType.Carousel; 24 | Columns = columns ?? new List(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ConfirmTemplate.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace LineMessagingAPISDK.Models 6 | { 7 | /// 8 | /// Template message with two action buttons. 9 | /// 10 | public class ConfirmTemplate : Template 11 | { 12 | private string text; 13 | 14 | /// 15 | /// Message text 16 | /// Max: 240 characters 17 | /// text will be truncated if it exceeds 240 characters. 18 | /// 19 | [JsonProperty("text")] 20 | public string Text 21 | { 22 | get { return text; } 23 | set { text = value?.Length > 240 ? value.Substring(0, 240) : value; } 24 | } 25 | 26 | [JsonProperty("actions")] 27 | public List Actions { get; set; } 28 | 29 | public ConfirmTemplate(string text = null, List actions = null) 30 | { 31 | Type = TemplateType.Confirm; 32 | this.Text = text; 33 | this.Actions = actions ?? new List(); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/DateTimePickerMode.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum DatetimePickerMode { Date, Time, Datetime } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/DateTimePickerTemplateAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace LineMessagingAPISDK.Models 6 | { 7 | /// 8 | /// When this action is tapped, the URI specified in the uri field is opened. 9 | /// 10 | public class DatetimePickerTemplateAction : TemplateAction 11 | { 12 | /// 13 | /// String returned via webhook in the postback.data property of the postback event 14 | /// Max: 300 characters 15 | /// 16 | [StringLength(300, ErrorMessage = "Max: 300 characters")] 17 | [JsonProperty("data")] 18 | public string Data { get; set; } 19 | 20 | /// 21 | /// Action mode 22 | /// date: Pick date 23 | /// time: Pick time 24 | /// datetime: Pick date and time 25 | /// 26 | [JsonProperty("mode")] 27 | public DatetimePickerMode Mode { get; set; } 28 | 29 | /// 30 | /// Initial value of date or time 31 | /// 32 | [JsonProperty("initial")] 33 | public string Initial { get; set; } 34 | 35 | /// 36 | /// Largest date or time value that can be selected. 37 | /// Must be greater than the min value. 38 | /// 39 | [JsonProperty("max")] 40 | public string Max { get; set; } 41 | 42 | /// 43 | /// Smallest date or time value that can be selected. 44 | /// Must be less than the max value. 45 | /// 46 | [JsonProperty("min")] 47 | public string Min { get; set; } 48 | 49 | public DatetimePickerTemplateAction(string label, string data, DatetimePickerMode mode, string initial="", string max = "", string min = "") 50 | { 51 | if(!string.IsNullOrEmpty(max) && !string.IsNullOrEmpty(min)) 52 | { 53 | if (DateTime.Parse(max) < DateTime.Parse(min)) 54 | throw new Exception("min must be less than the max value"); 55 | } 56 | 57 | Type = TemplateActionType.Datetimepicker; 58 | this.Label = label; 59 | this.Data = data; 60 | this.Mode = mode; 61 | this.Initial = initial; 62 | this.Max = max; 63 | this.Min = min; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Event.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | public class Event 7 | { 8 | [JsonProperty("replyToken")] 9 | public string ReplyToken { get; set; } 10 | 11 | [JsonProperty("type")] 12 | public EventType Type { get; set; } 13 | 14 | [JsonProperty("timestamp")] 15 | public long Timestamp { get; set; } 16 | 17 | [JsonProperty("source")] 18 | public Source Source { get; set; } 19 | 20 | [JsonProperty("message", TypeNameHandling = TypeNameHandling.Auto)] 21 | public object Message { get; set; } 22 | 23 | [JsonProperty("postback")] 24 | public Postback Postback { get; set; } 25 | 26 | [JsonProperty("beacon")] 27 | public Beacon Beacon { get; set; } 28 | 29 | public ReplyMessage CreateReply(string text = null, Message message = null) 30 | { 31 | ReplyMessage replyMessage = new ReplyMessage(); 32 | 33 | replyMessage.ReplyToken = this.ReplyToken; 34 | 35 | if (!string.IsNullOrEmpty(text)) 36 | replyMessage.Messages.Add(new TextMessage(text)); 37 | if (message != null) 38 | replyMessage.Messages.Add(message); 39 | return replyMessage; 40 | } 41 | 42 | public ReplyMessage CreateReply(string text = null, List messages = null) 43 | { 44 | ReplyMessage replyMessage = new ReplyMessage(); 45 | 46 | replyMessage.ReplyToken = this.ReplyToken; 47 | 48 | if (!string.IsNullOrEmpty(text)) 49 | replyMessage.Messages.Add(new TextMessage(text)); 50 | if (messages != null) 51 | replyMessage.Messages.AddRange(messages); 52 | return replyMessage; 53 | } 54 | 55 | public PushMessage CreatePush(string text = null, Message message = null) 56 | { 57 | PushMessage pushMessage = new PushMessage(); 58 | 59 | pushMessage.To = this.Source.UserId ?? this.Source.GroupId ?? this.Source.RoomId; 60 | 61 | if (!string.IsNullOrEmpty(text)) 62 | pushMessage.Messages.Add(new TextMessage(text)); 63 | if (message != null) 64 | pushMessage.Messages.Add(message); 65 | return pushMessage; 66 | } 67 | 68 | public PushMessage CreatePush(string text = null, List messages = null) 69 | { 70 | PushMessage pushMessage = new PushMessage(); 71 | 72 | pushMessage.To = this.Source.UserId ?? this.Source.GroupId ?? this.Source.RoomId; 73 | 74 | if (!string.IsNullOrEmpty(text)) 75 | pushMessage.Messages.Add(new TextMessage(text)); 76 | if (messages != null) 77 | pushMessage.Messages.AddRange(messages); 78 | return pushMessage; 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/EventType.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum EventType { Beacon, Message, Follow, Unfollow, Join, Leave, Postback } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ImageCarouselTemplate.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK.Validators; 2 | using Newtonsoft.Json; 3 | using System; 4 | using System.Collections.Generic; 5 | 6 | namespace LineMessagingAPISDK.Models 7 | { 8 | /// 9 | /// Template with multiple images which can be cycled like a carousel.. 10 | /// 11 | public class ImageCarouselTemplate : Template 12 | { 13 | 14 | /// 15 | /// Array of columns 16 | /// Max: 10 17 | /// 18 | [ItemCounts(10, ErrorMessage = "You can store up to 10 Columns")] 19 | [JsonProperty("columns")] 20 | public List Columns { get; set; } 21 | 22 | public ImageCarouselTemplate(List columns = null) 23 | { 24 | Type = TemplateType.Image_carousel; 25 | Columns = columns ?? new List(); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ImageColumn.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK.Validators; 2 | using Newtonsoft.Json; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace LineMessagingAPISDK.Models 7 | { 8 | /// 9 | /// Column for Carousel Template 10 | /// 11 | public class ImageColumn 12 | { 13 | /// 14 | /// Image URL (Max: 1000 characters) 15 | /// HTTPS 16 | /// JPEG or PNG 17 | /// Aspect ratio: 1:1 18 | /// Max width: 1024px 19 | /// Max: 1 MB 20 | /// 21 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 22 | [RegularExpression("^https://.*(jpg|jpeg|png)$", ErrorMessage = "Require HTTPS and jpeg/png")] 23 | [JsonProperty("imageUrl")] 24 | public string ImageUrl { get; set; } 25 | 26 | /// 27 | /// Action when image is tapped 28 | /// 29 | [JsonProperty("action")] 30 | public TemplateAction Action { get; set; } 31 | 32 | public ImageColumn(string imageUrl, TemplateAction action) 33 | { 34 | this.ImageUrl = imageUrl; 35 | this.Action = action; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ImageMapAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | /// 6 | /// Object which specifies the actions and tappable regions of an imagemap. 7 | /// When a region is tapped, the user is redirected to the URI specified in uri and the message specified in message is sent. 8 | /// 9 | public abstract class ImageMapAction 10 | { 11 | [JsonProperty("type")] 12 | public ImageMapActionType Type { get; set; } 13 | 14 | [JsonProperty("area")] 15 | public ImageMapArea Area { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ImageMapActionType.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum ImageMapActionType { Message, Uri } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ImageMapArea.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace LineMessagingAPISDK.Models 9 | { 10 | /// 11 | /// Defines the size of the full imagemap with the width as 1040px. The top left is used as the origin of the area. 12 | /// 13 | public class ImageMapArea 14 | { 15 | /// 16 | /// Horizontal position of the tappable area 17 | /// 18 | [JsonProperty("x")] 19 | public double X { get; set; } 20 | 21 | /// 22 | /// Vertical position of the tappable area 23 | /// 24 | [JsonProperty("y")] 25 | public double Y { get; set; } 26 | 27 | /// 28 | /// Width of the tappable area 29 | /// 30 | [JsonProperty("width")] 31 | public double Width { get; set; } 32 | 33 | /// 34 | /// Height of the tappable area 35 | /// 36 | [JsonProperty("height")] 37 | public double Height { get; set; } 38 | 39 | public ImageMapArea(double x, double y, double width, double height) 40 | { 41 | this.X = x; 42 | this.Y = y; 43 | this.Width = width; 44 | this.Height = height; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ImageMapMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace LineMessagingAPISDK.Models 6 | { 7 | /// 8 | /// Imagemaps are images with one or more links. You can assign one link for the entire image or multiple links which correspond to different regions of the image 9 | /// 10 | public class ImageMapMessage : Message 11 | { 12 | /// 13 | /// To use an imagemap, you must include URLs with the image width (px) at the end of the base URL so that the client can download the image at the required resolution. 14 | /// For example, if the base URL is, 15 | /// https://example.com/images/cats 16 | /// the URL for a client device to download a 700px image would be 17 | /// https://example.com/images/cats/700 18 | /// Below are the image resolutions required by client devices. 19 | /// •Width: 240px, 300px, 460px, 700px, 1040px 20 | /// The image used for the imagemap must meet the following specifications: 21 | /// •Image format: JPEG or PNG 22 | /// •File size: Up to 1 MB 23 | /// 24 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 25 | [RegularExpression("^https://", ErrorMessage = "Require HTTPS")] 26 | [JsonProperty("baseUrl")] 27 | public string BaseUrl { get; set; } 28 | 29 | private string altText; 30 | /// 31 | /// Alternative text 32 | /// Max: 400 characters 33 | /// text will be truncated if it exceeds 400 characters. 34 | [JsonProperty("altText")] 35 | public string AltText 36 | { 37 | get { return altText; } 38 | set { altText = value?.Length > 400 ? value.Substring(0, 400) : value; } 39 | } 40 | 41 | /// 42 | /// Defines the size of the full imagemap with the width as 1040px. The top left is used as the origin of the area. 43 | /// 44 | [JsonProperty("baseSize")] 45 | public BaseSize BaseSize { get; set; } 46 | 47 | /// 48 | /// Object which specifies the actions and tappable regions of an imagemap. 49 | /// When a region is tapped, the user is redirected to the URI specified in uri and the message specified in message is sent. 50 | /// 51 | [JsonProperty("actions")] 52 | public List Actions { get; set; } 53 | 54 | public ImageMapMessage(string baseUrl = "", string altText = "", BaseSize baseSize = null, List actions = null) 55 | { 56 | Type = MessageType.Imagemap; 57 | this.BaseUrl = baseUrl; 58 | this.AltText = altText; 59 | this.BaseSize = baseSize; 60 | this.Actions = actions ?? new List(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ImageMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | public class ImageMessage : Message 7 | { 8 | /// 9 | /// Image URL (Max: 1000 characters) 10 | /// HTTPS 11 | /// JPEG 12 | /// Max: 1024 x 1024 13 | /// Max: 1 MB 14 | /// 15 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 16 | [RegularExpression("^https://.*(jpg|jpeg)$", ErrorMessage = "Require HTTPS and jpeg")] 17 | [JsonProperty("originalContentUrl")] 18 | public string OriginalContentUrl { get; set; } 19 | 20 | /// 21 | /// Preview image URL (Max: 1000 characters) 22 | /// HTTPS 23 | /// JPEG 24 | /// Max: 240 x 240 25 | /// Max: 1 MB 26 | /// 27 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 28 | [RegularExpression("^https://.*(jpg|jpeg)$", ErrorMessage = "Require HTTPS and jpeg")] 29 | [JsonProperty("previewImageUrl")] 30 | public string PreviewImageUrl { get; set; } 31 | 32 | public ImageMessage(string originalContentUrl, string previewImageUrl) 33 | { 34 | Type = MessageType.Image; 35 | this.OriginalContentUrl = originalContentUrl.Replace("http://", "https://"); 36 | this.PreviewImageUrl = previewImageUrl; 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/LocationMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using System; 4 | 5 | namespace LineMessagingAPISDK.Models 6 | { 7 | public class LocationMessage : Message 8 | { 9 | private string title; 10 | /// 11 | /// Max: 100 characters 12 | /// title will be truncated if it exceeds 100 characters. 13 | /// 14 | [JsonProperty("title")] 15 | public string Title 16 | { 17 | get { return title; } 18 | set { title = value?.Length > 100 ? value.Substring(0, 100) : value; } 19 | } 20 | 21 | private string address; 22 | 23 | /// 24 | /// Max: 100 characters 25 | /// address will be truncated if it exceeds 100 characters. 26 | /// 27 | [JsonProperty("address")] 28 | public string Address 29 | { 30 | get { return address; } 31 | set { address = value?.Length > 100 ? value.Substring(0, 100) : value; } 32 | } 33 | 34 | [JsonProperty("latitude")] 35 | public double? Latitude { get; set; } 36 | 37 | [JsonProperty("longitude")] 38 | public double? Longitude { get; set; } 39 | 40 | public LocationMessage(string title, string address, double? latitude, double? longitude) 41 | { 42 | Type = MessageType.Location; 43 | this.Title = title; 44 | this.Address = address; 45 | this.Latitude = latitude; 46 | this.Longitude = longitude; 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Media.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace LineMessagingAPISDK.Models 9 | { 10 | public class Media 11 | { 12 | public Stream Content { get; set; } 13 | public string ContentType { get; set; } 14 | public string FileName { get; set; } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/MemberIdsResponse.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class MemberIdsResponse 6 | { 7 | [JsonProperty("memberIds")] 8 | public string[] MemberIds { get; set; } 9 | 10 | [JsonProperty("next")] 11 | public string Next { get; set; } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Message.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using System; 4 | 5 | namespace LineMessagingAPISDK.Models 6 | { 7 | public class Message 8 | { 9 | [JsonProperty("id")] 10 | public string Id { get; set; } 11 | 12 | [JsonProperty("type")] 13 | public MessageType Type { get; set; } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/MessageImageMapAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class MessageImageMapAction : ImageMapAction 6 | { 7 | private string text; 8 | 9 | /// 10 | /// Message to send 11 | /// Max: 400 characters 12 | /// 13 | [JsonProperty("text")] 14 | public string Text 15 | { 16 | get { return text; } 17 | set { text = value?.Length > 400 ? value.Substring(0, 400) : value; } 18 | } 19 | 20 | public MessageImageMapAction(string text, ImageMapArea area) 21 | { 22 | this.Type = ImageMapActionType.Message; 23 | this.Text = text; 24 | this.Area = area; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/MessageTemplateAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | /// 7 | /// When this action is tapped, the string in the text field is sent as a message from the user. 8 | /// 9 | public class MessageTemplateAction : TemplateAction 10 | { 11 | private string text; 12 | /// 13 | /// Text sent when the action is performed 14 | /// Max: 300 characters 15 | /// text will be truncated if it exceeds 300 characters. 16 | /// 17 | [JsonProperty("text")] 18 | public string Text 19 | { 20 | get { return text; } 21 | set { text = value?.Length > 300 ? value.Substring(0, 300) : value; } 22 | } 23 | 24 | public MessageTemplateAction(string label, string text) 25 | { 26 | Type = TemplateActionType.Message; 27 | this.Label = label; 28 | this.Text = text; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/MessageType.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum MessageType { Text, Image, Video, Audio, Location, Sticker, Imagemap, Template } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/MulticastMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | /// 7 | /// Send messages to multiple users at any time. 8 | /// 9 | public class MulticastMessage 10 | { 11 | /// 12 | /// IDs of the receivers 13 | /// Max: 150 users 14 | /// Use IDs returned via the webhook event of source users. IDs of groups or rooms cannot be used. 15 | /// Do not use the LINE ID found on the LINE app. 16 | /// 17 | [JsonProperty("to")] 18 | public List To { get; set; } 19 | 20 | /// 21 | /// Messages 22 | /// Max: 5 23 | /// 24 | [JsonProperty("messages")] 25 | public List Messages { get; set; } = new List(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Postback.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | /// 6 | /// Event object for when a user performs an action on a template message which initiates a postback. You can reply to postback events. 7 | /// 8 | public class Postback 9 | { 10 | /// 11 | /// Postback data 12 | /// 13 | [JsonProperty("data")] 14 | public string Data { get; set; } 15 | 16 | /// 17 | /// Object with the date and time selected by a user through a datetime picker action. The full-date, time-hour, and time-minute formats follow the RFC3339 protocol. 18 | /// 19 | [JsonProperty("params")] 20 | public PostbackParams Params { get; set; } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/PostbackParams.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class PostbackParams 6 | { 7 | /// 8 | /// full-date 9 | /// Date selected by user. Only included in the date mode. 10 | /// 11 | [JsonProperty("date")] 12 | public string Date { get; set; } 13 | 14 | /// 15 | /// time-hour ":" time-minute 16 | /// Time selected by the user.Only included in the time mode. 17 | /// 18 | [JsonProperty("time")] 19 | public string Time { get; set; } 20 | 21 | /// 22 | /// full-date "T" time-hour ":" time-minute 23 | /// Date and time selected by the user. Only included in the datetime mode. 24 | /// 25 | [JsonProperty("datetime")] 26 | public string DateTime { get; set; } 27 | } 28 | } -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/PostbackTemplateAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | /// 7 | /// When this action is tapped, a postback event is returned via webhook with the specified string in the data field. 8 | /// If you have included the text field, the string in the text field is sent as a message from the user. 9 | /// 10 | public class PostbackTemplateAction : TemplateAction 11 | { 12 | private string data; 13 | /// 14 | /// String returned via webhook in the postback.data property of the postback event 15 | /// Max: 300 characters 16 | /// data will be truncated if it exceeds 300 characters. 17 | /// 18 | [JsonProperty("data")] 19 | public string Data 20 | { 21 | get { return data; } 22 | set { data = value?.Length > 300 ? value.Substring(0, 300) : value; } 23 | } 24 | 25 | private string text; 26 | /// 27 | /// Text sent when the action is performed 28 | /// Max: 300 characters 29 | /// text will be truncated if it exceeds 300 characters. 30 | /// 31 | [JsonProperty("text")] 32 | public string Text 33 | { 34 | get { return text; } 35 | set { text = value?.Length > 300 ? value.Substring(0, 300) : value; } 36 | } 37 | 38 | public PostbackTemplateAction(string data, string label = "", string text = "") 39 | { 40 | Type = TemplateActionType.Postback; 41 | this.Label = label; 42 | this.Data = data; 43 | this.Text = text; 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Profile.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class Profile 6 | { 7 | [JsonProperty("displayName")] 8 | public string DisplayName { get; set; } 9 | 10 | [JsonProperty("userId")] 11 | public string UserId { get; set; } 12 | 13 | [JsonProperty("pictureUrl")] 14 | public string PictureUrl { get; set; } 15 | 16 | [JsonProperty("statusMessage")] 17 | public string StatusMessage { get; set; } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/PushMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.Collections.Generic; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | /// 7 | /// Send messages to a user, group, or room at any time. 8 | /// 9 | public class PushMessage 10 | { 11 | [JsonProperty("to")] 12 | public string To { get; set; } 13 | 14 | [JsonProperty("messages")] 15 | public List Messages { get; set; } = new List(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/ReplyMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace LineMessagingAPISDK.Models 6 | { 7 | /// 8 | /// Respond to events from users, groups, and rooms. 9 | /// Webhooks are used to notify you when an event occurs.For events that you can respond to, a replyToken is issued for replying to messages. 10 | /// Because the replyToken becomes invalid after a certain period of time, responses should be sent as soon as a message is received.Reply tokens can only be used once. 11 | /// 12 | public class ReplyMessage 13 | { 14 | [JsonProperty("replyToken")] 15 | public string ReplyToken { get; set; } 16 | 17 | [JsonProperty("messages")] 18 | public List Messages { get; set; } = new List(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/RichMenu.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace LineMessagingAPISDK.Models 7 | { 8 | public class RichMenu 9 | { 10 | /// 11 | /// Rich menu ID 12 | /// 13 | [JsonProperty("richMenuId")] 14 | public string RichMenuId { get; set; } 15 | 16 | /// 17 | /// size object which contains the width and height of the rich menu displayed in the chat. Rich menu images must be one of the following sizes: 2500x1686, 2500x843. 18 | /// 19 | [JsonProperty("size")] 20 | public RichMenuSize Size { get; set; } 21 | 22 | /// 23 | /// true to display the rich menu by default. Otherwise, false. 24 | /// 25 | [JsonProperty("selected")] 26 | public bool Selected { get; set; } 27 | 28 | /// 29 | /// Name of the rich menu. This value can be used to help manage your rich menus and is not displayed to users. Maximum of 300 characters. 30 | /// 31 | [StringLength(300, ErrorMessage = "Max: 300 characters")] 32 | [JsonProperty("name")] 33 | public string Name { get; set; } 34 | 35 | /// 36 | /// Text displayed in the chat bar. Maximum of 14 characters. 37 | /// 38 | [StringLength(14, ErrorMessage = "Max: 14 characters")] 39 | [JsonProperty("chatBarText")] 40 | public string ChatBarText { get; set; } 41 | 42 | /// 43 | /// Array of area objects which define the coordinates and size of tappable areas. Maximum of 20 area objects. 44 | /// 45 | [JsonProperty("areas")] 46 | public List Areas { get; set; } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/RichMenuArea.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class RichMenuArea 6 | { 7 | /// 8 | /// Object describing the boundaries of the area in pixels. See bounds object. 9 | /// 10 | [JsonProperty("bounds")] 11 | public RichMenuBounds Bounds { get; set; } 12 | 13 | [JsonProperty("action")] 14 | public TemplateAction Action { get; set; } 15 | } 16 | } -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/RichMenuBounds.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class RichMenuBounds 6 | { 7 | [JsonProperty("x")] 8 | public int X = 2500; 9 | 10 | [JsonProperty("y")] 11 | public int Y = 2500; 12 | 13 | [JsonProperty("width")] 14 | public int Width = 2500; 15 | 16 | [JsonProperty("height")] 17 | public int Height = 2500; 18 | 19 | public RichMenuBounds(int x, int y, int width, int height) 20 | { 21 | this.X = x; 22 | this.Y = y; 23 | this.Width = width; 24 | this.Height = height; 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/RichMenuSize.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System.ComponentModel.DataAnnotations; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | public class RichMenuSize 7 | { 8 | /// 9 | /// Width of the rich menu. Must be 2500. 10 | /// 11 | [JsonProperty("width")] 12 | public int Width = 2500; 13 | 14 | /// 15 | /// Height of the rich menu. Possible values: 1686, 843. 16 | /// 17 | [JsonProperty("height")] 18 | public int Height { get; set; } 19 | 20 | public RichMenuSize(int height) 21 | { 22 | this.Height = height; 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Source.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class Source 6 | { 7 | [JsonProperty("type")] 8 | public SourceType Type { get; set; } 9 | 10 | /// 11 | /// ID of the source user 12 | /// 13 | [JsonProperty("userId")] 14 | public string UserId { get; set; } 15 | 16 | /// 17 | /// ID of the source group 18 | /// 19 | [JsonProperty("groupId")] 20 | public string GroupId { get; set; } 21 | 22 | /// 23 | /// ID of the source room 24 | /// 25 | [JsonProperty("roomId")] 26 | public string RoomId { get; set; } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/SourceType.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum SourceType { User, Group, Room } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/StickerMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | /// 6 | /// For a list of the sticker IDs for stickers that can be sent with the Messaging API, see Sticker list. https://devdocs.line.me/files/sticker_list.pdf 7 | /// 8 | public class StickerMessage : Message 9 | { 10 | [JsonProperty("packageId")] 11 | public string PackageId { get; set; } 12 | 13 | [JsonProperty("stickerId")] 14 | public string StickerId { get; set; } 15 | 16 | public StickerMessage(string packageId, string stickerId) 17 | { 18 | Type = MessageType.Sticker; 19 | this.PackageId = packageId; 20 | this.StickerId = stickerId; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/Template.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public abstract class Template 6 | { 7 | [JsonProperty("type")] 8 | public TemplateType Type { get; internal set; } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/TemplateAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | 4 | namespace LineMessagingAPISDK.Models 5 | { 6 | /// 7 | /// Action to include in your template message. 8 | /// 9 | public abstract class TemplateAction 10 | { 11 | [JsonProperty("type")] 12 | public TemplateActionType Type { get; set; } 13 | 14 | private string label; 15 | 16 | /// 17 | /// Label for the action 18 | /// Max: 20 characters 19 | /// label will be truncated if it exceeds 20 characters. 20 | /// 21 | [JsonProperty("label")] 22 | public string Label 23 | { 24 | get { return label; } 25 | set { label = value?.Length > 20 ? value.Substring(0, 20) : value; } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/TemplateActionType.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum TemplateActionType { Postback, Uri, Message, Datetimepicker } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/TemplateColumn.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK.Validators; 2 | using Newtonsoft.Json; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace LineMessagingAPISDK.Models 7 | { 8 | /// 9 | /// Column for Carousel Template 10 | /// 11 | public class TemplateColumn 12 | { 13 | /// 14 | /// Image URL (Max: 1000 characters) 15 | /// HTTPS 16 | /// JPEG or PNG 17 | /// Aspect ratio: 1:1.51 18 | /// Max width: 1024px 19 | /// Max: 1 MB 20 | /// 21 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 22 | [RegularExpression("^https://.*(jpg|jpeg|png)$", ErrorMessage = "Require HTTPS and jpeg/png")] 23 | [JsonProperty("thumbnailImageUrl")] 24 | public string ThumbnailImageUrl { get; set; } 25 | 26 | private string title; 27 | /// 28 | /// Max: 40 characters 29 | /// text will be truncated if it exceeds 40 characters. 30 | /// 31 | [JsonProperty("title")] 32 | public string Title 33 | { 34 | get { return title; } 35 | set { title = value?.Length > 40 ? value.Substring(0, 40) : value; } 36 | } 37 | 38 | private string text; 39 | /// 40 | /// Message text 41 | /// Max: 120 characters(no image or title) 42 | /// Max: 60 characters(message with an image or title) 43 | /// text will be truncated if it exceeds its limit. 44 | /// 45 | [JsonProperty("text")] 46 | public string Text 47 | { 48 | get { return text; } 49 | set 50 | { 51 | if (string.IsNullOrEmpty(ThumbnailImageUrl) && string.IsNullOrEmpty(Title)) 52 | text = value?.Length > 120 ? value.Substring(0, 120) : value; 53 | else 54 | text = value?.Length > 60 ? value.Substring(0, 60) : value; 55 | } 56 | } 57 | 58 | /// 59 | /// Action when tapped 60 | /// Max: 3 61 | /// 62 | [ItemCounts(3, ErrorMessage = "You can store up to 3 actions")] 63 | [JsonProperty("actions")] 64 | public List Actions { get; set; } = new List(); 65 | 66 | public TemplateColumn(string thumbnailImageUrl = null, string title = null, string text = null, List actions = null) 67 | { 68 | this.ThumbnailImageUrl = thumbnailImageUrl; 69 | this.Title = title; 70 | this.Text = text; 71 | this.Actions = actions ?? new List(); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/TemplateMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | /// 6 | /// Template messages are messages with predefined layouts which you can customize. There are three types of templates available that can be used to interact with users through your bot. 7 | /// 8 | public class TemplateMessage : Message 9 | { 10 | private string altText; 11 | 12 | /// 13 | /// Alternative text 14 | /// Max: 400 characters 15 | /// text will be truncated if it exceeds 400 characters. 16 | [JsonProperty("altText")] 17 | public string AltText 18 | { 19 | get { return altText; } 20 | set { altText = value?.Length > 400 ? value.Substring(0, 400) : value; } 21 | } 22 | 23 | /// 24 | /// Object with the contents of the template. 25 | /// 26 | [JsonProperty("template")] 27 | public Template Template { get; set; } 28 | 29 | public TemplateMessage(string altText, Template template) 30 | { 31 | Type = MessageType.Template; 32 | this.AltText = altText; 33 | this.Template = template; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/TemplateType.cs: -------------------------------------------------------------------------------- 1 | namespace LineMessagingAPISDK.Models 2 | { 3 | public enum TemplateType { Buttons, Confirm, Carousel, Image_carousel } 4 | } 5 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/TextMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | /// 6 | /// Message object which contains the text sent from the source or send to user. 7 | /// 8 | public class TextMessage : Message 9 | { 10 | private string text; 11 | 12 | /// 13 | /// Message text 14 | /// Max: 2000 characters 15 | /// text will be truncated if it exceeds 2000 characters. 16 | /// 17 | [JsonProperty("text")] 18 | public string Text 19 | { 20 | get { return text; } 21 | set { text = value?.Length > 2000 ? value.Substring(0, 2000) : value; } 22 | } 23 | 24 | public TextMessage(string text) 25 | { 26 | Type = MessageType.Text; 27 | this.Text = text; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/URIImageMapAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | 3 | namespace LineMessagingAPISDK.Models 4 | { 5 | public class UriImageMapAction : ImageMapAction 6 | { 7 | [JsonProperty("linkUri")] 8 | public string LinkUri { get; set; } 9 | 10 | public UriImageMapAction(string linkUri, ImageMapArea area) 11 | { 12 | this.Type = ImageMapActionType.Uri; 13 | this.LinkUri = linkUri; 14 | this.Area = area; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/UriTemplateAction.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.ComponentModel.DataAnnotations; 4 | 5 | namespace LineMessagingAPISDK.Models 6 | { 7 | /// 8 | /// When this action is tapped, the URI specified in the uri field is opened. 9 | /// 10 | public class UriTemplateAction : TemplateAction 11 | { 12 | /// 13 | /// URI opened when the action is performed (Max: 1000 characters) 14 | /// http, https, tel 15 | /// 16 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 17 | [RegularExpression("^(https|http|tel):", ErrorMessage = "Should starts with https, http or tel")] 18 | [JsonProperty("uri")] 19 | public string Uri { get; set; } 20 | 21 | public UriTemplateAction(string label, string uri) 22 | { 23 | Type = TemplateActionType.Uri; 24 | this.Label = label; 25 | this.Uri = uri; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Models/VideoMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Converters; 3 | using System; 4 | using System.ComponentModel.DataAnnotations; 5 | 6 | namespace LineMessagingAPISDK.Models 7 | { 8 | public class VideoMessage : Message 9 | { 10 | /// 11 | /// URL of video file (Max: 1000 characters) 12 | /// HTTPS 13 | /// mp4 14 | /// Less than 1 minute 15 | /// Max: 10 MB 16 | /// 17 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 18 | [RegularExpression("^https://.*(mp4|mpeg4)$", ErrorMessage = "Require HTTPS and mp4")] 19 | [JsonProperty("originalContentUrl")] 20 | public string OriginalContentUrl { get; set; } 21 | 22 | /// 23 | /// Preview image URL (Max: 1000 characters) 24 | /// HTTPS 25 | /// JPEG 26 | /// Max: 240 x 240 27 | /// Max: 1 MB 28 | /// 29 | [StringLength(1000, ErrorMessage = "Max: 1000 characters")] 30 | [RegularExpression("^https://.*(jpeg|jpg)$", ErrorMessage = "Require HTTPS and jpg")] 31 | [JsonProperty("previewImageUrl")] 32 | public string PreviewImageUrl { get; set; } 33 | 34 | public VideoMessage(string originalContentUrl, string previewImageUrl) 35 | { 36 | Type = MessageType.Video; 37 | this.OriginalContentUrl = originalContentUrl; 38 | this.PreviewImageUrl = previewImageUrl; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/LineMessagingAPISDK/Validators/ItemCountsAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.ComponentModel.DataAnnotations; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace LineMessagingAPISDK.Validators 10 | { 11 | public class ItemCountsAttribute : ValidationAttribute 12 | { 13 | private int maxCount; 14 | public ItemCountsAttribute(int maxCount) 15 | { 16 | this.maxCount = maxCount; 17 | } 18 | 19 | public override bool Equals(object obj) 20 | { 21 | var list = obj as IList; 22 | if (list != null) 23 | { 24 | return list.Count <= maxCount; 25 | } 26 | return true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LineMessagingAPISDK/README.md: -------------------------------------------------------------------------------- 1 | # LineMessagingAPISDK 2 | SDK of the LINE Messaging API for C#. 3 | 4 | About the LINE Messaging API 5 | ------------------------ 6 | 7 | See the official API documentation for more information. 8 | 9 | English: https://devdocs.line.me/en/
10 | Japanese: https://devdocs.line.me/ja/ 11 | 12 | ##Install 13 | You can install package from Nuget. 14 | > Install-Package LineMessagingAPI.CSharp 15 | 16 | You can also use Visual Studio 2015 Template for jump start.
17 | See [here](https://github.com/kenakamu/line-bot-sdk-csharp/tree/master/LineBotApplication) for more detail. 18 | 19 | # LineClient 20 | LineClient class is the main proxy class. 21 | 22 | #### Instantiate 23 | ```csharp 24 | LineClient lineClient = new LineClient(""); 25 | ``` 26 | #### Get Meida Content 27 | To download image, video or audio, use GetContent method. See https://devdocs.line.me/en/#get-content for more detail. 28 | ```csharp 29 | Media media = await lineClient.GetContent(""); 30 | ``` 31 | 32 | #### Get Line User Profile 33 | You can get Line User Profile. See https://devdocs.line.me/en/#bot-api-get-profile for more detail. 34 | ```csharp 35 | Profile profile = await lineClient.GetProflie(""); 36 | ``` 37 | 38 | #### Reply Message 39 | You can reply to Line Platform either by using reply or push. See https://devdocs.line.me/en/#reply-message for more detail. 40 | ```csharp 41 | await lineClient.ReplyToActivityAsync(replyMessage); 42 | ``` 43 | 44 | #### Push Message. 45 | You can reply to Line Platform either by using reply or push. See https://devdocs.line.me/en/#push-message for more detail. 46 | ```csharp 47 | await lineClient.PushAsync(replyMessage); 48 | ``` 49 | 50 | # Line Event 51 | LineEvent class contains incoming message. See https://devdocs.line.me/en/#webhook-event-object for detail. 52 | 53 | #### Create Reply 54 | You can create reply easily from LineEvent. 55 | ```csharp 56 | var reply = lineEvent.CreateReply(replyMessage); 57 | ``` 58 | 59 | # Create Reply Message 60 | Following examples demonstrate how to construct reply message. 61 | See https://devdocs.line.me/en/#send-message-object for detail of each message type. 62 | 63 | #### Text Reply 64 | ```csharp 65 | var replyMessage = new TextMessage(textMessage.Text); 66 | ``` 67 | 68 | #### Image Reply 69 | ```csharp        70 | var replyMessage = new ImageMessage("https://github.com/apple-touch-icon.png", "https://github.com/apple-touch-icon.png"); 71 | ``` 72 | 73 | #### Audio Reply 74 | ```csharp 75 | var replyMessage = new AudioMessage("pass to mp4 file", 3600); 76 | ``` 77 | 78 | #### Video Reply 79 | ```csharp 80 | var replyMessage = new VideoMessage("pass to video", "pass to video thumbnail"); 81 | ``` 82 | 83 | #### Sticker Reply 84 | ```csharp 85 | var replyMessage = new StickerMessage("1","1"); 86 | ``` 87 | 88 | #### Location Reply 89 | ```csharp 90 | var replyMessage = new LocationMessage("Title","Address","",""); 91 | ``` 92 | 93 | #### Button Template Reply 94 | ```csharp 95 | List actions = new List(); 96 | actions.Add(new MessageTemplateAction("Message Label", "sample data"));  97 | actions.Add(new PostbackTemplateAction("Postback Label", "sample data")); 98 | actions.Add(new UriTemplateAction("Uri Label", "https://github.com/kenakamu")); 99 | 100 | ButtonsTemplate buttonsTemplate = new ButtonsTemplate("https://github.com/apple-touch-icon.png", "Sample Title", "Sample Text", actions);                101 | var replyMessage = new TemplateMessage("Buttons", buttonsTemplate); 102 | ``` 103 | 104 | #### Confirm Template Reply 105 | ```csharp 106 | List actions = new List(); 107 | actions.Add(new MessageTemplateAction("Yes", "yes")); 108 | actions.Add(new MessageTemplateAction("No", "no")); 109 | 110 | ConfirmTemplate confirmTemplate = new ConfirmTemplate("Confirm Test", actions); 111 | replyMessage = new TemplateMessage("Confirm", confirmTemplate); 112 | ``` 113 | 114 | #### Carousel Template Reply 115 | ```csharp 116 | List columns = new List(); 117 | List actions = new List();  118 | actions.Add(new MessageTemplateAction("Message Label", "sample data")); 119 | actions.Add(new PostbackTemplateAction("Postback Label", "sample data")); 120 | actions.Add(new UriTemplateAction("Uri Label", "https://github.com/kenakamu"));                121 | 122 | columns.Add(new TemplateColumn() { Title = "Casousel 1 Title", Text = "Casousel 1 Text", ThumbnailImageUrl = "https://github.com/apple-touch-icon.png", Actions = actions });    123 | columns.Add(new TemplateColumn() { Title = "Casousel 2 Title", Text = "Casousel 2 Text", ThumbnailImageUrl = "https://github.com/apple-touch-icon.png", Actions = actions });  124 | 125 | CarouselTemplate carouselTemplate = new CarouselTemplate(columns);                126 | replyMessage = new TemplateMessage("Carousel", carouselTemplate); 127 | ``` 128 | 129 | #### Release Note 130 | v1.2 131 | - Support Multicast 132 | - Add more comments 133 | - Bug fixes 134 | v1.1 135 | - Fix download content bug 136 | v1.0 137 | - Initial Release 138 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26228.9 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LineWithBotFrameworkApplication", "LineWithBotFrameworkApplication\LineWithBotFrameworkApplication.csproj", "{931620E0-810D-42E7-8FD9-DAB87E0204E9}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {931620E0-810D-42E7-8FD9-DAB87E0204E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {931620E0-810D-42E7-8FD9-DAB87E0204E9}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {931620E0-810D-42E7-8FD9-DAB87E0204E9}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {931620E0-810D-42E7-8FD9-DAB87E0204E9}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/App_Start/WebApiConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web.Http; 5 | 6 | namespace $safeprojectname$ 7 | { 8 | public static class WebApiConfig 9 | { 10 | public static void Register(HttpConfiguration config) 11 | { 12 | // Web API configuration and services 13 | 14 | // Web API routes 15 | config.MapHttpAttributeRoutes(); 16 | 17 | config.Routes.MapHttpRoute( 18 | name: "DefaultApi", 19 | routeTemplate: "api/{controller}/{id}", 20 | defaults: new { id = RouteParameter.Optional } 21 | ); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Controllers/LineMessagesController.cs: -------------------------------------------------------------------------------- 1 | using LineMessagingAPISDK; 2 | using LineMessagingAPISDK.Models; 3 | using $safeprojectname$.Services; 4 | using Microsoft.Bot.Connector.DirectLine; 5 | using Newtonsoft.Json; 6 | using Newtonsoft.Json.Linq; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.Configuration; 10 | using System.Linq; 11 | using System.Net; 12 | using System.Net.Http; 13 | using System.Security.Cryptography; 14 | using System.Text; 15 | using System.Threading.Tasks; 16 | using System.Web.Http; 17 | using dl = Microsoft.Bot.Connector.DirectLine; 18 | using lm = LineMessagingAPISDK.Models; 19 | 20 | namespace $safeprojectname$.Controllers 21 | { 22 | public class LineMessagesController : ApiController 23 | { 24 | /// 25 | /// POST: api/Messages 26 | /// Receive a message from a user and reply to it 27 | /// 28 | public async Task Post(HttpRequestMessage request) 29 | { 30 | if (!await VaridateSignature(request)) 31 | return Request.CreateResponse(HttpStatusCode.BadRequest); 32 | 33 | lm.Activity activity = JsonConvert.DeserializeObject 34 | (await request.Content.ReadAsStringAsync()); 35 | 36 | // Line may send multiple events in one message, so need to handle them all. 37 | foreach (Event lineEvent in activity.Events) 38 | { 39 | LineMessageHandler handler = new LineMessageHandler(lineEvent); 40 | await handler.Initialize(); 41 | Profile profile = await handler.GetProfile(lineEvent.Source.UserId); 42 | 43 | switch (lineEvent.Type) 44 | { 45 | case EventType.Beacon: 46 | await handler.HandleBeaconEvent(); 47 | break; 48 | case EventType.Follow: 49 | await handler.HandleFollowEvent(); 50 | break; 51 | case EventType.Join: 52 | await handler.HandleJoinEvent(); 53 | break; 54 | case EventType.Leave: 55 | await handler.HandleLeaveEvent(); 56 | break; 57 | case EventType.Message: 58 | Message message = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 59 | switch (message.Type) 60 | { 61 | case MessageType.Text: 62 | await handler.HandleTextMessage(); 63 | break; 64 | case MessageType.Audio: 65 | case MessageType.Image: 66 | case MessageType.Video: 67 | await handler.HandleMediaMessage(); 68 | break; 69 | case MessageType.Sticker: 70 | await handler.HandleStickerMessage(); 71 | break; 72 | case MessageType.Location: 73 | await handler.HandleLocationMessage(); 74 | break; 75 | } 76 | break; 77 | case EventType.Postback: 78 | await handler.HandlePostbackEvent(); 79 | break; 80 | case EventType.Unfollow: 81 | await handler.HandleUnfollowEvent(); 82 | break; 83 | } 84 | } 85 | 86 | return Request.CreateResponse(HttpStatusCode.OK); 87 | } 88 | 89 | private async Task VaridateSignature(HttpRequestMessage request) 90 | { 91 | var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(ConfigurationManager.AppSettings["ChannelSecret"].ToString())); 92 | var computeHash = hmac.ComputeHash(Encoding.UTF8.GetBytes(await request.Content.ReadAsStringAsync())); 93 | var contentHash = Convert.ToBase64String(computeHash); 94 | var headerHash = Request.Headers.GetValues("X-Line-Signature").First(); 95 | 96 | return contentHash == headerHash; 97 | } 98 | } 99 | 100 | public class LineMessageHandler 101 | { 102 | private Event lineEvent; 103 | private static string directLineSecret = ConfigurationManager.AppSettings["DirectLineSecret"].ToString(); 104 | private LineClient lineClient = new LineClient(ConfigurationManager.AppSettings["ChannelToken"].ToString()); 105 | private DirectLineClient dlClient = new DirectLineClient(directLineSecret); 106 | private string conversationId; // DirectLine ConversationId 107 | private string watermark; // Limit the messages to get from DirectLine 108 | private Dictionary userParams; 109 | 110 | public LineMessageHandler(Event lineEvent) 111 | { 112 | this.lineEvent = lineEvent; 113 | } 114 | 115 | public async Task Initialize() 116 | { 117 | var lineId = lineEvent.Source.UserId ?? lineEvent.Source.GroupId ?? lineEvent.Source.RoomId; 118 | 119 | if (CacheService.caches.Keys.Contains(lineId)) 120 | { 121 | // Get preserved ConversationId and Watermark from cache. 122 | // If we scale out, then we have to use different method 123 | userParams = CacheService.caches[lineId] as Dictionary; 124 | conversationId = userParams.Keys.Contains("ConversationId") ? userParams["ConversationId"].ToString() : ""; 125 | watermark = userParams.Keys.Contains("Watermark") ? userParams["Watermark"].ToString() : null; 126 | } 127 | else 128 | { 129 | // If no cache, then create new one. 130 | userParams = new Dictionary(); 131 | var conversation = await dlClient.Conversations.StartConversationAsync(); 132 | userParams["ConversationId"] = conversationId = conversation.ConversationId; 133 | CacheService.caches[lineId] = userParams; 134 | watermark = null; 135 | } 136 | } 137 | 138 | public async Task HandleBeaconEvent() 139 | { 140 | } 141 | 142 | public async Task HandleFollowEvent() 143 | { 144 | } 145 | 146 | public async Task HandleJoinEvent() 147 | { 148 | } 149 | 150 | public async Task HandleLeaveEvent() 151 | { 152 | } 153 | 154 | public async Task HandlePostbackEvent() 155 | { 156 | dl.Activity sendMessage = new dl.Activity() 157 | { 158 | Type = "message", 159 | Text = lineEvent.Postback.Data, 160 | From = new dl.ChannelAccount(lineEvent.Source.UserId, lineEvent.Source.UserId) 161 | }; 162 | 163 | // Send the message, then fetch and reply messages, 164 | await dlClient.Conversations.PostActivityAsync(conversationId, sendMessage); 165 | await GetAndReplyMessages(); 166 | } 167 | 168 | public async Task HandleUnfollowEvent() 169 | { 170 | } 171 | 172 | public async Task GetProfile(string mid) 173 | { 174 | return await lineClient.GetProfile(mid); 175 | } 176 | 177 | public async Task HandleTextMessage() 178 | { 179 | var textMessage = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 180 | 181 | dl.Activity sendMessage = new dl.Activity() 182 | { 183 | Type = "message", 184 | Text = textMessage.Text, 185 | From = new dl.ChannelAccount(lineEvent.Source.UserId, lineEvent.Source.UserId) 186 | }; 187 | 188 | // Send the message, then fetch and reply messages, 189 | try 190 | { 191 | await dlClient.Conversations.PostActivityAsync(conversationId, sendMessage); 192 | } 193 | catch(Exception ex) 194 | { 195 | 196 | } 197 | await GetAndReplyMessages(); 198 | } 199 | 200 | public async Task HandleMediaMessage() 201 | { 202 | Message message = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 203 | // Get media from Line server. 204 | var media = await lineClient.GetContent(message.Id); 205 | await dlClient.Conversations.UploadAsync(conversationId, media.Content, lineEvent.Source.UserId, media.ContentType); 206 | await GetAndReplyMessages(); 207 | } 208 | 209 | public async Task HandleStickerMessage() 210 | { 211 | //https://devdocs.line.me/files/sticker_list.pdf 212 | var stickerMessage = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 213 | var message = new StickerMessage("1", "1"); 214 | await Reply(new List() { message }); 215 | } 216 | 217 | public async Task HandleLocationMessage() 218 | { 219 | var locationMessage = JsonConvert.DeserializeObject(lineEvent.Message.ToString()); 220 | 221 | dl.Activity sendMessage = new dl.Activity() 222 | { 223 | Type = "message", 224 | Text = locationMessage.Title, 225 | From = new dl.ChannelAccount(lineEvent.Source.UserId, lineEvent.Source.UserId), 226 | Entities = new List() 227 | { 228 | new Entity() 229 | { 230 | Type = "Place", 231 | Properties = JObject.FromObject(new Place(address:locationMessage.Address, 232 | geo:new dl.GeoCoordinates( 233 | latitude: locationMessage.Latitude, 234 | longitude: locationMessage.Longitude, 235 | name: locationMessage.Title), 236 | name:locationMessage.Title)) 237 | } 238 | } 239 | }; 240 | 241 | // Send the message, then fetch and reply messages, 242 | await dlClient.Conversations.PostActivityAsync(conversationId, sendMessage); 243 | await GetAndReplyMessages(); 244 | } 245 | 246 | private async Task Reply(List replyMessages) 247 | { 248 | int i = 0; 249 | try 250 | { 251 | await lineClient.ReplyToActivityAsync(lineEvent.CreateReply( 252 | messages: replyMessages.Take(5).ToList())); 253 | 254 | if (replyMessages.Count > 5) 255 | { 256 | i = 1; 257 | while (replyMessages.Count > i * 5) 258 | { 259 | await lineClient.PushAsync(lineEvent.CreatePush( 260 | messages: replyMessages.Skip(i * 5).Take(5).ToList())); 261 | i++; 262 | } 263 | } 264 | } 265 | catch 266 | { 267 | try 268 | { 269 | while (replyMessages.Count > i * 5) 270 | { 271 | await lineClient.PushAsync(lineEvent.CreatePush( 272 | messages: replyMessages.Skip(i * 5).Take(5).ToList())); 273 | i++; 274 | } 275 | } 276 | catch (Exception ex) 277 | { 278 | #if DEBUG 279 | await lineClient.PushAsync(lineEvent.CreatePush(ex.Message, message:null)); 280 | #endif 281 | } 282 | } 283 | } 284 | 285 | /// 286 | /// Get all messages from DirectLine and reply back to Line 287 | /// 288 | private async Task GetAndReplyMessages() 289 | { 290 | dl.ActivitySet result = string.IsNullOrEmpty(watermark) ? 291 | await dlClient.Conversations.GetActivitiesAsync(conversationId) : 292 | await dlClient.Conversations.GetActivitiesAsync(conversationId, watermark); 293 | 294 | userParams["Watermark"] = (Int64.Parse(result.Watermark)).ToString(); 295 | 296 | foreach (var activity in result.Activities) 297 | { 298 | if (activity.From.Id == lineEvent.Source.UserId) 299 | continue; 300 | 301 | List messages = new List(); 302 | 303 | if (activity.Attachments != null && activity.Attachments.Count != 0 && (activity.AttachmentLayout == null || activity.AttachmentLayout == "list") ) 304 | { 305 | foreach (var attachment in activity.Attachments) 306 | { 307 | if (attachment.ContentType.Contains("card.animation")) 308 | { 309 | // https://docs.botframework.com/en-us/core-concepts/reference/#animationcard 310 | // Use TextMessage for title and use Image message for image. Not really an animation though. 311 | AnimationCard card = JsonConvert.DeserializeObject(attachment.Content.ToString()); 312 | messages.Add(new TextMessage($"{card.Title}\r\n{card.Subtitle}\r\n{card.Text}")); 313 | foreach (var media in card.Media) 314 | { 315 | var originalContentUrl = media.Url?.Replace("http://", "https://"); 316 | var previewImageUrl = card.Image?.Url?.Replace("http://", "https://"); 317 | messages.Add(new ImageMessage(originalContentUrl, previewImageUrl)); 318 | } 319 | } 320 | else if (attachment.ContentType.Contains("card.audio")) 321 | { 322 | // https://docs.botframework.com/en-us/core-concepts/reference/#audiocard 323 | // Use TextMessage for title and use Audio message for image. 324 | AudioCard card = JsonConvert.DeserializeObject(attachment.Content.ToString()); 325 | messages.Add(new TextMessage($"{card.Title}\r\n{card.Subtitle}\r\n{card.Text}")); 326 | 327 | foreach (var media in card.Media) 328 | { 329 | var originalContentUrl = media.Url?.Replace("http://", "https://"); 330 | var durationInMilliseconds = 1; 331 | 332 | messages.Add(new AudioMessage(originalContentUrl, durationInMilliseconds)); 333 | } 334 | } 335 | else if (attachment.ContentType.Contains("card.hero") || attachment.ContentType.Contains("card.thumbnail")) 336 | { 337 | // https://docs.botframework.com/en-us/core-concepts/reference/#herocard 338 | // https://docs.botframework.com/en-us/core-concepts/reference/#thumbnailcard 339 | HeroCard hcard = null; 340 | 341 | if (attachment.ContentType.Contains("card.hero")) 342 | hcard = JsonConvert.DeserializeObject(attachment.Content.ToString()); 343 | else if (attachment.ContentType.Contains("card.thumbnail")) 344 | { 345 | ThumbnailCard tCard = JsonConvert.DeserializeObject(attachment.Content.ToString()); 346 | hcard = new HeroCard(tCard.Title, tCard.Subtitle, tCard.Text, tCard.Images, tCard.Buttons, null); 347 | } 348 | 349 | ButtonsTemplate buttonsTemplate = new ButtonsTemplate( 350 | hcard.Images?.First().Url.Replace("http://","https://"), 351 | hcard.Subtitle == null ? null : hcard.Title, 352 | string.IsNullOrEmpty(hcard.Subtitle) ? hcard.Text : hcard.Subtitle); 353 | 354 | if (hcard.Buttons != null) 355 | { 356 | foreach (var button in hcard.Buttons) 357 | { 358 | buttonsTemplate.Actions.Add(GetAction(button)); 359 | } 360 | } 361 | else 362 | { 363 | // Action is mandatory, so create from title/subtitle. 364 | var actionLabel = hcard.Title?.Length < hcard.Subtitle?.Length ? hcard.Title : hcard.Subtitle; 365 | buttonsTemplate.Actions.Add(new PostbackTemplateAction(actionLabel, actionLabel, actionLabel)); 366 | } 367 | 368 | messages.Add(new TemplateMessage("Buttons template", buttonsTemplate)); 369 | } 370 | else if (attachment.ContentType.Contains("receipt")) 371 | { 372 | // https://docs.botframework.com/en-us/core-concepts/reference/#receiptcard 373 | // Use TextMessage and Buttons. As LINE doesn't support thumbnail type yet. 374 | 375 | ReceiptCard card = JsonConvert.DeserializeObject(attachment.Content.ToString()); 376 | var text = card.Title + "\r\n\r\n"; 377 | foreach(var fact in card.Facts) 378 | { 379 | text += $"{fact.Key}:{fact.Value}\r\n"; 380 | } 381 | text += "\r\n"; 382 | foreach(var item in card.Items) 383 | { 384 | text += $"{item.Title}\r\nprice:{item.Price},quantity:{item.Quantity}"; 385 | } 386 | 387 | messages.Add(new TextMessage(text)); 388 | 389 | ButtonsTemplate buttonsTemplate = new ButtonsTemplate(title:$"total:{card.Total}", text:$"tax:{card.Tax}"); 390 | foreach (var button in card.Buttons) 391 | { 392 | buttonsTemplate.Actions.Add(GetAction(button)); 393 | } 394 | 395 | messages.Add(new TemplateMessage("Buttons template", buttonsTemplate)); 396 | } 397 | else if (attachment.ContentType.Contains("card.signin")) 398 | { 399 | // https://docs.botframework.com/en-us/core-concepts/reference/#signincard 400 | // Line doesn't support auth button yet, so simply represent link. 401 | SigninCard card = JsonConvert.DeserializeObject(attachment.Content.ToString()); 402 | 403 | ButtonsTemplate buttonsTemplate = new ButtonsTemplate(text: card.Text); 404 | foreach (var button in card.Buttons) 405 | { 406 | buttonsTemplate.Actions.Add(GetAction(button)); 407 | } 408 | messages.Add(new TemplateMessage("Buttons template", buttonsTemplate)); 409 | } 410 | else if (attachment.ContentType.Contains("card.video")) 411 | { 412 | // https://docs.botframework.com/en-us/core-concepts/reference/#videocard 413 | // Use Video message for video and buttons for action. 414 | 415 | VideoCard card = JsonConvert.DeserializeObject(attachment.Content.ToString()); 416 | 417 | foreach (var media in card.Media) 418 | { 419 | var originalContentUrl = media?.Url?.Replace("http://", "https://"); 420 | var previewImageUrl = card.Image?.Url?.Replace("http://", "https://"); 421 | 422 | messages.Add(new VideoMessage(originalContentUrl, previewImageUrl)); 423 | } 424 | 425 | ButtonsTemplate buttonsTemplate = new ButtonsTemplate(title: card.Title, text: $"{card.Subtitle}\r\n{card.Text}"); 426 | foreach (var button in card.Buttons) 427 | { 428 | buttonsTemplate.Actions.Add(GetAction(button)); 429 | } 430 | messages.Add(new TemplateMessage("Buttons template", buttonsTemplate)); 431 | } 432 | else if (attachment.ContentType.Contains("image")) 433 | { 434 | var originalContentUrl = attachment.ContentUrl?.Replace("http://", "https://"); 435 | var previewImageUrl = string.IsNullOrEmpty(attachment.ThumbnailUrl) ? attachment.ContentUrl?.Replace("http://", "https://") : attachment.ThumbnailUrl?.Replace("http://", "https://"); 436 | 437 | messages.Add(new ImageMessage(originalContentUrl, previewImageUrl)); 438 | } 439 | else if (attachment.ContentType.Contains("audio")) 440 | { 441 | var originalContentUrl = attachment.ContentUrl?.Replace("http://", "https://"); 442 | var durationInMilliseconds = 0; 443 | 444 | messages.Add(new AudioMessage(originalContentUrl, durationInMilliseconds)); 445 | } 446 | else if (attachment.ContentType.Contains("video")) 447 | { 448 | var originalContentUrl = attachment.ContentUrl?.Replace("http://", "https://"); 449 | var previewImageUrl = attachment.ThumbnailUrl?.Replace("http://", "https://"); 450 | 451 | messages.Add(new VideoMessage(originalContentUrl, previewImageUrl)); 452 | } 453 | } 454 | } 455 | else if (activity.Attachments != null && activity.Attachments.Count != 0 && activity.AttachmentLayout != null) 456 | { 457 | CarouselTemplate carouselTemplate = new CarouselTemplate(); 458 | 459 | foreach (var attachment in activity.Attachments) 460 | { 461 | HeroCard hcard = null; 462 | 463 | if (attachment.ContentType == "application/vnd.microsoft.card.hero") 464 | hcard = JsonConvert.DeserializeObject(attachment.Content.ToString()); 465 | else if (attachment.ContentType == "application/vnd.microsoft.card.thumbnail") 466 | { 467 | ThumbnailCard tCard = JsonConvert.DeserializeObject(attachment.Content.ToString()); 468 | hcard = new HeroCard(tCard.Title, tCard.Subtitle, tCard.Text, tCard.Images, tCard.Buttons, null); 469 | } 470 | else 471 | continue; 472 | 473 | TemplateColumn tColumn = new TemplateColumn( 474 | hcard.Images.FirstOrDefault()?.Url?.Replace("http://", "https://"), 475 | hcard.Subtitle == null ? null : hcard.Title, 476 | string.IsNullOrEmpty(hcard.Subtitle) ? hcard.Title : hcard.Subtitle); 477 | 478 | if (hcard.Buttons != null) 479 | { 480 | foreach (var button in hcard.Buttons) 481 | { 482 | tColumn.Actions.Add(GetAction(button)); 483 | } 484 | } 485 | else 486 | { 487 | // Action is mandatory, so create from title/subtitle. 488 | var actionLabel = hcard.Title?.Length < hcard.Subtitle?.Length ? hcard.Title : hcard.Subtitle; 489 | tColumn.Actions.Add(new PostbackTemplateAction(actionLabel, actionLabel, actionLabel)); 490 | } 491 | 492 | carouselTemplate.Columns.Add(tColumn); 493 | } 494 | 495 | messages.Add(new TemplateMessage("Carousel template", carouselTemplate)); 496 | } 497 | else if (activity.Entities != null && activity.Entities.Count != 0) 498 | { 499 | foreach (var entity in activity.Entities) 500 | { 501 | switch (entity.Type) 502 | { 503 | case "Place": 504 | Place place = entity.Properties.ToObject(); 505 | GeoCoordinates geo = JsonConvert.DeserializeObject(place.Geo.ToString()); 506 | messages.Add(new LocationMessage(place.Name, place.Address.ToString(), geo.Latitude, geo.Longitude)); 507 | break; 508 | case "GeoCoordinates": 509 | GeoCoordinates geoCoordinates = entity.Properties.ToObject(); 510 | messages.Add(new LocationMessage(activity.Text, geoCoordinates.Name, geoCoordinates.Latitude, geoCoordinates.Longitude)); 511 | break; 512 | } 513 | } 514 | } 515 | else if (activity.ChannelData != null) 516 | { 517 | } 518 | else if (!string.IsNullOrEmpty(activity.Text)) 519 | { 520 | if (activity.Text.Contains("\n\n*")) 521 | { 522 | var lines = activity.Text.Split(new[] { "\n\n" }, StringSplitOptions.RemoveEmptyEntries); 523 | ButtonsTemplate buttonsTemplate = new ButtonsTemplate(text: lines[0]); 524 | 525 | foreach (var line in lines.Skip(1)) 526 | { 527 | buttonsTemplate.Actions.Add(new PostbackTemplateAction(line, line.Replace("* ", ""), line.Replace("* ", ""))); 528 | } 529 | 530 | messages.Add(new TemplateMessage("Buttons template", buttonsTemplate)); 531 | } 532 | else 533 | messages.Add(new TextMessage(activity.Text)); 534 | } 535 | 536 | await Reply(messages); 537 | } 538 | } 539 | 540 | /// 541 | /// Create TemplateAction from CardAction. 542 | /// 543 | /// CardAction 544 | /// TemplateAction 545 | private TemplateAction GetAction(CardAction button) 546 | { 547 | switch (button.Type) 548 | { 549 | case "openUrl": 550 | case "playAudio": 551 | case "playVideo": 552 | case "showImage": 553 | case "signin": 554 | case "downloadFile": 555 | return new UriTemplateAction(button.Title, button.Value.ToString()); 556 | case "imBack": 557 | return new MessageTemplateAction(button.Title, button.Value.ToString()); 558 | case "postBack": 559 | return new PostbackTemplateAction(button.Title, button.Value.ToString(), button.Value.ToString()); 560 | default: 561 | return null; 562 | } 563 | } 564 | } 565 | } 566 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="$safeprojectname$.WebApiApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | using System.Web.Http; 6 | using System.Web.Routing; 7 | 8 | namespace $safeprojectname$ 9 | { 10 | public class WebApiApplication : System.Web.HttpApplication 11 | { 12 | protected void Application_Start() 13 | { 14 | GlobalConfiguration.Configure(WebApiConfig.Register); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/LineWithBotFrameworkApplication.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | Debug 8 | AnyCPU 9 | 10 | 11 | 2.0 12 | {931620E0-810D-42E7-8FD9-DAB87E0204E9} 13 | {349c5851-65df-11da-9384-00065b846f21};{fae04ec0-301f-11d3-bf4b-00c04f79efbc} 14 | Library 15 | Properties 16 | Line_with_BotFramework_Application 17 | LineWithBotFrameworkApplication 18 | v4.6.2 19 | true 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | true 32 | full 33 | false 34 | bin\ 35 | DEBUG;TRACE 36 | prompt 37 | 4 38 | 39 | 40 | pdbonly 41 | true 42 | bin\ 43 | TRACE 44 | prompt 45 | 4 46 | 47 | 48 | 49 | ..\packages\Autofac.4.6.0\lib\net45\Autofac.dll 50 | 51 | 52 | ..\packages\Chronic.Signed.0.3.2\lib\net40\Chronic.dll 53 | True 54 | 55 | 56 | ..\packages\LineMessagingAPI.CSharp.1.6.0\lib\LineMessagingAPISDK.dll 57 | 58 | 59 | ..\packages\Microsoft.Bot.Connector.DirectLine.3.0.2\lib\net45\Microsoft.Bot.Connector.DirectLine.dll 60 | 61 | 62 | ..\packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.5\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll 63 | 64 | 65 | 66 | ..\packages\Microsoft.IdentityModel.Protocol.Extensions.1.0.4.403061554\lib\net45\Microsoft.IdentityModel.Protocol.Extensions.dll 67 | 68 | 69 | ..\packages\Microsoft.Rest.ClientRuntime.2.3.8\lib\net452\Microsoft.Rest.ClientRuntime.dll 70 | 71 | 72 | ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll 73 | 74 | 75 | ..\packages\System.IdentityModel.Tokens.Jwt.4.0.4.403061554\lib\net45\System.IdentityModel.Tokens.Jwt.dll 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | ..\packages\Microsoft.AspNet.WebApi.Client.5.2.3\lib\net45\System.Net.Http.Formatting.dll 97 | 98 | 99 | ..\packages\Microsoft.AspNet.WebApi.Core.5.2.3\lib\net45\System.Web.Http.dll 100 | 101 | 102 | ..\packages\Microsoft.AspNet.WebApi.WebHost.5.2.3\lib\net45\System.Web.Http.WebHost.dll 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | Global.asax 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | Web.config 123 | 124 | 125 | Web.config 126 | 127 | 128 | 129 | 130 | 131 | 132 | 10.0 133 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | True 143 | True 144 | 1590 145 | / 146 | http://localhost:1590/ 147 | False 148 | False 149 | 150 | 151 | False 152 | 153 | 154 | 155 | 156 | 157 | 158 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 159 | 160 | 161 | 162 | 163 | 170 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("$safeprojectname$")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("$safeprojectname$")] 13 | [assembly: AssemblyCopyright("Copyright © 2017")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("931620e0-810d-42e7-8fd9-dab87e0204e9")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Services/CacheService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Web; 5 | 6 | namespace $safeprojectname$.Services 7 | { 8 | public static class CacheService 9 | { 10 | public static Dictionary caches; 11 | 12 | static CacheService() 13 | { 14 | caches = new Dictionary(); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Web.Debug.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Web.Release.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 17 | 18 | 19 | 30 | 31 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/LineWithBotFrameworkApplication/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LineWithBotFrameworkApplication/README.md: -------------------------------------------------------------------------------- 1 | # LineWithBotFrameworkApplition 2 | Visual Studio 2015 project template which connects Line Messaging Web API to Microsoft Bot Framework via Directline. 3 | If you already have Bot Application up and running at Microsoft Bor Framework, then use this tempalte. 4 | 5 | About the LINE Messaging API 6 | ------------------------ 7 | 8 | See the official API documentation for more information. 9 | 10 | English: https://devdocs.line.me/en/
11 | Japanese: https://devdocs.line.me/ja/ 12 | 13 | ## Install 14 | You can download from https://github.com/kenakamu/line-bot-sdk-csharp/releases 15 | 16 | 1. Download Line Bot Application.zip from [here](https://github.com/kenakamu/line-bot-sdk-csharp/releases) and 17 | save the zip file to your Visual Studio 2015 templates directory which is traditionally in 18 | "%USERPROFILE%\Documents\Visual Studio 2015\Templates\ProjectTemplates\Visual C#\" 19 | 2. Open Visual Studio 2015 20 | 3. Create a new C# project using the new Line Bot Application template. 21 | 4. Update ChannelSecret, ChannelToken and DirectLine Secrete in Web.Config file.
22 | You can get there values from https://business.line.me and https://dev.botframework.com 23 | 24 | ## Update Bot Framework Code 25 | As directline doesn't support several features, you need to move Attachments or Entity to ChannelData. 26 | 27 | #### Carousel template 28 | Simply move Attachements to ChannelData by using following code. 29 | ```csharp 30 | // Assume you already create reply which contains carousel as Attachments 31 | if (channelId.Contains("direct"))  32 | { 33 | // Create new reply message 34 | var replyForLine = context.MakeMessage(); 35 | // Copy Attachment from original message to ChannelData 36 | // Mark the type as "Rich" 37 | Entity heroCard = new Entity() { 38 | Type = "Rich", 39 | Properties = new JObject(new JProperty("Attachments", JArray.FromObject(reply.Attachments))) };                40 | replyForLine.ChannelData = JsonConvert.SerializeObject(heroCard); 41 | } 42 | ``` 43 | 44 | #### Confirm Template 45 | Use the same sample above but specify "Confirm" as Type. 46 | 47 | #### Location and GeoCoordinates 48 | In BotFramework, you set Location and GeoCoordinates as Entity. Place them into ChannelData and mark Type as "Location" or "GeoCoordinates" depending on type. 49 | ```csharp 50 | // Assume you already create reply which contains place or geocoordinates as Entity 51 | if (channelId.Contains("direct")) 52 | { 53 | // Copy Entity from original message to ChannelData  54 | reply.ChannelData = JsonConvert.SerializeObject(entity); 55 | } 56 | ``` 57 | 58 | # Template features 59 | Basically, you dont neet to do anything when using this templates as it simply redirect request frm Line Platform to Microsoft Bot Framework. 60 | 61 | ### Varidate Signature 62 | When receiving Post message from Line Platform, it verifies the request by following the documentation at https://devdocs.line.me/en/#signature-validation 63 | 64 | ### Parse and handle Incoming message 65 | Parse the incoming Post body and handle event. LineMessageHandler class contains methods to handle each type. 66 | 67 | ### Redirect to DirectLine 68 | Create appropriate message and send it to DirectLine. 69 | 70 | ### Get reply from DirectLine 71 | Once message redirected, then receiving message. 72 | 73 | ### Reply/Push message 74 | Finally, it replies to Line Platform by using either reply or push. Find detail information here. 75 | 76 | Reply Message: https://devdocs.line.me/en/#reply-message
77 | Push Message: https://devdocs.line.me/en/#push-message 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # !!!!This repository is abondoned. Please use https://github.com/pierre3/LineMessagingApi instead for C# LINE Bot development.!!!! 2 | 3 | To consolidate the community effor, i decided to move all the assets into @pierre3 repository. All the development effort will be done in that repository in the future. We also look for other developers who want to work together! 4 | --------------------------------------------------------------------------------