{"id":13429919,"url":"https://github.com/Dolfik1/Funogram","last_synced_at":"2025-03-16T04:31:21.230Z","repository":{"id":39545239,"uuid":"89526097","full_name":"Dolfik1/Funogram","owner":"Dolfik1","description":"F# Telegram Bot Api library","archived":false,"fork":false,"pushed_at":"2024-10-18T13:03:43.000Z","size":959,"stargazers_count":104,"open_issues_count":0,"forks_count":20,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-10-18T15:32:36.743Z","etag":null,"topics":["fsharp","telegram-bot-api","telegram-bots"],"latest_commit_sha":null,"homepage":null,"language":"F#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Dolfik1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2017-04-26T20:59:43.000Z","updated_at":"2024-10-18T13:03:47.000Z","dependencies_parsed_at":"2024-01-05T20:52:15.268Z","dependency_job_id":"7b3115b5-16e3-4cc0-8bfc-cd25e35b78db","html_url":"https://github.com/Dolfik1/Funogram","commit_stats":{"total_commits":163,"total_committers":16,"mean_commits":10.1875,"dds":"0.25153374233128833","last_synced_commit":"07d109720ebd18ed0f7fa612323e3b5104770178"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dolfik1%2FFunogram","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dolfik1%2FFunogram/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dolfik1%2FFunogram/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Dolfik1%2FFunogram/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Dolfik1","download_url":"https://codeload.github.com/Dolfik1/Funogram/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":221649932,"owners_count":16857667,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["fsharp","telegram-bot-api","telegram-bots"],"created_at":"2024-07-31T02:00:47.811Z","updated_at":"2025-03-16T04:31:21.223Z","avatar_url":"https://github.com/Dolfik1.png","language":"F#","funding_links":[],"categories":["Frameworks, Libraries and Tools","框架, 库和工具","Bot"],"sub_categories":["Bot","机器人"],"readme":"![.NET Core](https://github.com/Dolfik1/Funogram/workflows/.NET/badge.svg)\n[![NuGet](https://img.shields.io/nuget/v/Funogram.svg)](https://www.nuget.org/packages/Funogram/)\n[![NuGet](https://img.shields.io/nuget/v/Funogram.Telegram.svg)](https://www.nuget.org/packages/Funogram.Telegram/)\n[![NuGet](https://img.shields.io/badge/Bot%20API-8.3-blue?logo=telegram)](https://www.nuget.org/packages/Funogram.Telegram/)\n\n\u003cimg src=\"https://github.com/Dolfik1/Funogram/raw/master/docs/files/img/logo.png\" alt=\"Funogram Logo\" width=\"200\" align=\"right\" /\u003e\n\n## Introduction\nThis library provides F# interface to [Telegram Bot API](https://core.telegram.org/bots/api). The library has two parts. The first one is Funogram. It contains basic functions to interact with Telegram-like servers (REST interaction, serialization, etc). \nAnd the second part is Funogram.Telegram. It contains all Telegram methods and types for specified Telegram Bot API version.  \n\nIn addition to the pure API implementation, this library provides high-level functions to make the development of bots easy. These functions are located in `Funogram.Telegram.Bot` module.\n\n## Breaking changes\n\nFunogram.Telegram 6.0.0.x has breaking changes.\n* All request types are moved from `RequestsTypes` module to `Req` module;\n* Some types changed due to Telegram Bot API changes\n\n## Installation\nYou need to install latest Funogram and Funogram.Telegram packages from nuget:\n```shell\ndotnet add package Funogram\ndotnet add package Funogram.Telegram\n```\nInstallation for .NET Framework users:\n```shell\nInstall-Package Funogram\nInstall-Package Funogram.Telegram\n```\n\n## Examples\nYou may start learning Funogram from [example bots](https://github.com/Dolfik1/Funogram/tree/master/src/examples).\n\n## Hello, world!\nFirst of all you need to get an API key. See [Telegram Bot API documentation](https://core.telegram.org/bots#6-botfather).\n\nWhen Bot API token is registered you may start to write your first bot. Let's write simple bot that sends \"Hello, world!\" in reply to any message. You can find source code of the bot [here](https://github.com/Dolfik1/Funogram/tree/master/src/examples/Funogram.Examples.HelloWorld).\n\nLet's open the necessary namespaces:\n```f#\nopen Funogram.Api\nopen Funogram.Telegram\nopen Funogram.Telegram.Bot\n```\n\n\nThere are two mutually exclusive ways of receiving updates for your bot — the `getUpdates` method on one hand and `webhooks` on the other. Incoming updates are stored on the server until the bot receives them either way, but they will not be kept longer than 24 hours.\n\nThe Funogram library automatically receive updates and passes them to your updates handler. Let's write updates handle:\n```f#\nlet updateArrived (ctx: UpdateContext) =\n  match ctx.Update.Message with\n  | Some { MessageId = messageId; Chat = chat } -\u003e\n    Api.sendMessageReply chat.Id \"Hello, world!\" messageId \n    |\u003e api ctx.Config\n    |\u003e Async.Ignore\n    |\u003e Async.Start\n  | _ -\u003e ()\n```\n\nThis simple handler is checking if update contains `Message`. If a `Message` found in the `Update` then bot will send answer:\n```f#\nApi.sendMessageReply chat.Id \"Hello, world!\" messageId \n|\u003e api ctx.Config\n|\u003e Async.Ignore\n|\u003e Async.Start\n```\n\nThis code also might be written like this:\n```f#\nReq.SendMessage.Make(chat.Id, text = \"Hello, world!\", replyToMessageId = messageId) \n|\u003e api ctx.Config\n|\u003e Async.Ignore\n|\u003e Async.Start\n```\n\nNow we need to set bot token, pass update handler and start the bot. It might be done with few lines of code:\n```f#\n[\u003cEntryPoint\u003e]\nlet main _ =\n  async {\n    let config = Config.defaultConfig |\u003e Config.withReadTokenFromFile\n    let! _ = Api.deleteWebhookBase () |\u003e api config\n    return! startBot config updateArrived None\n  } |\u003e Async.RunSynchronously\n  0\n```\n\nLet's take a look at each line in detail. Firstly we need to setup bot token\n```f#\nlet config = Config.defaultConfig |\u003e Config.withReadTokenFromFile\n```\n\nThe default config looks like:\n```f#\nlet defaultConfig =\n  { Token = \"\"\n    Offset = Some 0L\n    Limit = Some 100\n    Timeout = Some 60000\n    AllowedUpdates = None\n    Client = new HttpClient()\n    ApiEndpointUrl = Uri(\"https://api.telegram.org/bot\")\n    WebHook = None\n    OnError = (fun e -\u003e printfn \"%A\" e) }\n```\n\nas you can notice there is no set token by default. There are two built-in ways to setup token:\n1. `Config.withReadTokenFromFile` function will read token from file named `token` (file should be located in run directory). If file does not exist the app will wait for token input in stdin and then save it to `token` file.\n2. `Config.withReadTokenFromEnv \"mytokenenv\"` function will read value from `mytokenenv` environment variable.\n\nYou also can specify token manually:\n```f#\nlet config = { Config.defaultConfig with Token = \"mysecrettoken\" }\n```\n\nWhen token is set we are ready to make requests. Let's invoke `deleteWebhook` function:\n\n```f#\nlet! _ = Api.deleteWebhookBase () |\u003e api config\n```\n\nThis function is not necessary but I always invoking it to force reset websocket configuration. If you have no plans to use websocket connection you can skip this step. Otherwise it may result to missing updates.\n\nFinally we are ready to start the bot loop:\n```f#\nreturn! startBot config updateArrived None\n```\n\nThis function will start main bot loop. For now on all updates will be collected and redirected to our update hook.\n\nYou may have noticed that `getUpdates` method returns Array of Update but in our handler we have only single update.\nThis is done for the convenience of the end developer. In most of cases you need to process updates sequentially. But if you want to process whole received updates you may pass hook function as last parameter of startBot. For example:\n```f#\nreturn! startBot config (fun _ -\u003e ()) ((fun updates -\u003e printfn \"Got updates array: %A\" updates) |\u003e Some)\n```\n\n## Processing commands\nFunogram can automatically parse specified commands with arguments:\n\n```f#\n\nlet updateArrived (ctx: UpdateContext) =\n  processCommands ctx [|\n    cmd \"/start\" (fun _ -\u003e printfn \"User invoked /start command!\")\n    cmdScan \"/say %s\" (fun text _ -\u003e printfn \"User invoked say command with text %s\" text)\n    cmdScan \"/sum %i %i\" (fun a b _ -\u003e printfn \"Sum of %i and %i is %i\" a b (a + b))\n  |] |\u003e ignore\n```\n\n`processCommands` function returns false if at least one of commands is matched. \n\nThere are two main functions: `cmd` and `cmdScan`. There are only one difference between both commands. `cmdScan` is used for command with arguments when the `cmd` is for command without arguments. Both functions has two parameters. First parameter is command itself (it may contain arguments in case of `cmdScan`). The second parameter is callback function.\n\n`cmdScan` works mostly like `sprintf` function but it's logic is reversed. The values will be extracted and passed as first parameter of callback function.\n\nCommands processor also correctly process commands with bot username. For example the are two valid commands for bot:\n* /start\n* /start@MyExampleFSharpBot\n\nwhen @MyExampleFSharpBot is bot's username.\n\nYou also may write your own command function with custom logic (if required) which may check text for keywords instead of command (see [Bot.fs](https://github.com/Dolfik1/Funogram/tree/master/src/Funogram.Telegram/Bot.fs) for reference) \n\n## Configure WebSockets\nIf you want to use webhooks, you should start application with admin privileges. The sample app is located [here](https://github.com/Dolfik1/Funogram/tree/master/src/examples/Funogram.Examples.WebSocket).\n\nTo get updates via webhooks you need send your endpoint address to Telegram server:\n```f#\nlet! hook = setWebhookBase webSocketEndpoint None None None |\u003e api config\n```\nYou can use [ngrok](https://ngrok.com/) service to test webhooks on your local machine that no have public address.\n\nThen you should set `WebHook` field in `BotConfig`. `WebHook` field have `BotWebHook` type that contains two fields: `Listener` and `ValidateRequest`:\n```f#\nlet apiPath = sprintf \"/%s\" config.Token\nlet webSocketEndpoint = sprintf \"https://1c0860ec2320.ngrok.io/%s\" webSocketEndpoint apiPath\nlet! hook = setWebhookBase webSocketEndpoint None None None |\u003e api config\nmatch hook with\n| Ok -\u003e\n  use listener = new HttpListener()\n  listener.Prefixes.Add(\"http://*:4444/\")\n  listener.Start()\n\n  let webhook = { Listener = listener; ValidateRequest = (fun req -\u003e req.Url.LocalPath = apiPath) }\n  return! startBot { config with WebHook = Some webhook } updateArrived None\n\n| Error e -\u003e \n  printf \"Can't set webhook: %A\" e\n  return ()\n```\nTelegram [recommends](https://core.telegram.org/bots/api#setwebhook) using a secret path in URL with your bot's token. You can validate telegram request in `ValidateRequest` function.\n\nNote: if you want to rollback to `getUpdates` method you need to clear webhook. This might be done via `deleteWebhook` function.\n\n## Codegen\nFunogram types are generated automatically from Telegram Bot API [reference](https://core.telegram.org/bots/api). The code generation tool is located in `src/Funogram.Generator` folder.\n\nTo start generator run Funogram.Generator project. You should specify project's directory as working directory to get correct result.\n\nTelegram Bot API reference will parsed and output will copied to `src/Funogram.Generator/out` folder. The generated files are:\n* types.json\n* methods.json\n\nThe generated code will be copied to `src/Funogram.Telegram` folder:\n* Types.fs\n* RequestsTypes.fs\n\nIf you want to generate types and methods for old Telegram server version you may specify link to [web archive](https://web.archive.org/web/*/https://core.telegram.org/bots/api).\n\nYou also can patch code to load reference data from *.json files. This will allow you to make changes if needed.\n\nThe generator supports remapping. This make it possible to replace some values in generated json to your own (see `RemapTypes.json` and `RemapMethods.json`)\n\n## Advanced\nThe library is built around types. Any API request or response is F# record type:\n```f#\ntype GetUpdates =\n  {\n    Offset: int64 option\n    Limit: int64 option\n    Timeout: int64 option\n    AllowedUpdates: string[] option\n  }\n  static member Make(?offset: int64, ?limit: int64, ?timeout: int64, ?allowedUpdates: string[]) = \n    {\n      Offset = offset\n      Limit = limit\n      Timeout = timeout\n      AllowedUpdates = allowedUpdates\n    }\n  interface IRequestBase\u003cUpdate[]\u003e with\n    member _.MethodName = \"getUpdates\"\n```\n\nThe request types should implement `IRequestBase\u003cT\u003e` interface when `T` is return type and `MethodName` is Telegram's Bot API method name.\nYou can create your own request type and send it to the server without patching Funogram.Telegram library. This might be useful if Funogram is not updated to latest Bot API version but you want to use some new features without waiting for new release.\n\nResponse types are also record types:\n```f#\n\n/// This object contains information about one answer option in a poll.\nand [\u003cCLIMutable\u003e] PollOption =\n  {\n    /// Option text, 1-100 characters\n    [\u003cDataMember(Name = \"text\")\u003e]\n    Text: string\n    /// Number of users that voted for this option\n    [\u003cDataMember(Name = \"voter_count\")\u003e]\n    VoterCount: int64\n  }\n  static member Create(text: string, voterCount: int64) = \n    {\n      Text = text\n      VoterCount = voterCount\n    }\n ```\n\nAll fields in type should be marked with `DataMember` attribute to avoid problems with parameters naming.\n\nTelegram Bot API have four ways of passing parameters in Bot API requests:\n\n* URL query string\n* application/x-www-form-urlencoded\n* application/json (except for uploading files)\n* multipart/form-data (use to upload files)\n\nThe library uses `URL query string` method if possible and `multipart/form-data` method for all requests with files. The preferred method will be choosen by request data. If you want to use application/json method for some reasons you can use `Funogram.Tools.Api.makeJsonBodyRequestAsync` function (this function will not work with files). You also can implement your own method of Bot API request (see `Funogram/Tools.fs` for reference) \n\n\n## Articles\n\nDEPRECATED: [Funogram: Writing Telegram Bots In F#](https://medium.com/@worldbeater/funogram-writing-telegram-bots-in-f-f27a873fa548)\n\nDEPRECATED: [Funogram.Keyboard: How to reserve seats on an airplane using F # and telegram](https://medium.com/@fsharpfan/funogram-keyboard-how-to-reserve-seats-on-an-airplane-using-f-and-telegram-6f7035e9c698)\n\n## Plugins and Extensions\n\nDEPRECATED: [Funogram.Keyboard](https://github.com/dohly/funogram.keyboard)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDolfik1%2FFunogram","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDolfik1%2FFunogram","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDolfik1%2FFunogram/lists"}