diff --git a/src/command.rs b/src/command.rs
index 31aa092e..446017e1 100644
--- a/src/command.rs
+++ b/src/command.rs
@@ -44,6 +44,24 @@ impl Command {
             String::from(prefix) + &self.name
         }
     }
+
+    pub fn get_matched_value2(
+        &self,
+        global_parameters: &CommandEnum,
+    ) -> (String, String) {
+        let prefix = if let Some(prefix) = &self.prefix {
+            prefix
+        } else if let Some(prefix) = &global_parameters.prefix {
+            prefix
+        } else {
+            "/"
+        };
+        if let Some(rule) = &global_parameters.rename_rule {
+            (String::from(prefix), rename_by_rule(&self.name, rule.as_str()))
+        } else {
+            (String::from(prefix), self.name.clone())
+        }
+    }
 }
 
 pub struct CommandAttrs {
diff --git a/src/lib.rs b/src/lib.rs
index ab3d04f8..06b3249f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -38,7 +38,7 @@ macro_rules! get_or_return {
     }
 }
 
-#[proc_macro_derive(BotCommand, attributes(command))]
+#[proc_macro_derive(BotCommands, attributes(command))]
 pub fn derive_telegram_command_enum(tokens: TokenStream) -> TokenStream {
     let input = parse_macro_input!(tokens as DeriveInput);
 
@@ -102,7 +102,7 @@ pub fn derive_telegram_command_enum(tokens: TokenStream) -> TokenStream {
     let fn_commands = impl_commands(&variant_infos, &command_enum);
 
     let trait_impl = quote! {
-        impl BotCommand for #ident {
+        impl BotCommands for #ident {
             #fn_descriptions
             #fn_parse
             #fn_commands
@@ -137,36 +137,26 @@ fn impl_descriptions(
     infos: &[Command],
     global: &CommandEnum,
 ) -> quote::__private::TokenStream {
-    let global_description = if let Some(s) = &global.description {
-        quote! { #s, "\n", }
-    } else {
-        quote! {}
-    };
-    let command = infos.iter().map(|c| c.get_matched_value(global));
-    let description =
-        infos.iter().map(|info| {
-            info.description
-                .as_deref()
-                .map(|e| {
-                    if e != "off" {
-                        format!(" - {}", e)
-                    } else {
-                        e.to_string()
-                    }
-                })
-                .unwrap_or_default()
-        });
-    let result_iter = command.zip(description).map(|(c, d)| {
-        if &d == "off" {
-            quote! {}
-        } else {
-            quote! { #c, #d, '\n', }
-        }
+    let command_descriptions = infos.iter().filter_map(|c| {
+        let (prefix, command) = c.get_matched_value2(global);
+        let description = c.description.clone().unwrap_or_default();
+        (description != "off").then(|| quote! { CommandDescription { prefix: #prefix, command: #command, description: #description } })
     });
 
+    let global_description = match global.description.as_deref() {
+        Some(gd) => quote! { .global_description(#gd) },
+        None => quote! {},
+    };
+
     quote! {
-        fn descriptions() -> String {
-            std::concat!(#global_description #(#result_iter)*).to_string()
+        fn descriptions() -> teloxide::utils::command::CommandDescriptions<'static> {
+            use teloxide::utils::command::{CommandDescriptions, CommandDescription};
+            use std::borrow::Cow;
+
+            CommandDescriptions::new(&[
+                #(#command_descriptions),*
+            ])
+            #global_description
         }
     }
 }