File size: 10,566 Bytes
a8e0f28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
import os
import re
import uuid
import tempfile
from datetime import datetime
from colorpaws import ColorPaws
from google import generativeai as genai
from markdown_pdf import MarkdownPdf, Section
from google.generativeai.types import GenerationConfig
from google.generativeai.types.helper_types import RequestOptions

class KhutbahMaker:
    """Copyright (C) 2025 Ikmal Said. All rights reserved"""
    
    def __init__(self, mode='default', api_key=None, model='gemini-2.0-flash-thinking-exp-01-21', timeout=180):
        """

        Initialize KhutbahMaker module.

        

        Parameters:

            mode (str): Startup mode ('default', 'webui', or 'api')

            api_key (str): API key for AI services

            model (str): AI model to use

            timeout (int): Timeout for AI requests in seconds

        """
        self.logger = ColorPaws(name=self.__class__.__name__, log_on=True, log_to=None)
        self.aigc_model = model
        self.api_key = api_key
        self.timeout = timeout * 1000
        
        self.logger.info("KhutbahMaker is ready!")
        
        if mode != 'default':
            if mode == 'webui':
                self.start_webui()
            else:
                raise ValueError(f"Invalid startup mode: {mode}")

    def __clean_markdown(self, text):
        """Clean up markdown text"""
        text = re.sub(r'```[a-zA-Z]*\n', '', text)
        text = re.sub(r'```\n?', '', text)
        return text.strip()

    def __generate_khutbah(self, topic, length, tone, language, task_id):
        """Generate khutbah content using AI"""
        try:
            if self.api_key:
                genai.configure(api_key=self.api_key)
            
            self.logger.info(f"[{task_id}] Generating khutbah: '{topic}' ({language} - {tone})")
            
            if length.lower() == 'short':
                length = 'short (approximately 10-15 minutes)'
            elif length.lower() == 'medium':
                length = 'medium (approximately 15-20 minutes)'
            elif length.lower() == 'long':
                length = 'long (approximately 20-30 minutes)'

            prompt = f"You are an expert Islamic scholar in writing khutbahs. You are required to write a {length} Friday khutbah (sermon) in {language} on the topic on '{topic}' with a {tone.lower()} tone. Create a complete, well-structured Islamic khutbah that includes: 1. An appropriate title 2. Opening with praise to Allah and salutations on Prophet Muhammad (peace be upon him) 3. Introduction to the topic with relevant Quranic verses and Hadith 4. Main body with clear points, explanations, and guidance 5. Practical advice for the audience 6. Conclusion with a summary of key points 7. Closing duas (prayers) The khutbah should be scholarly yet accessible, with proper citations of Quranic verses and authentic Hadith. Write the ENTIRE khutbah in {language} only, including the explanation and translation of Quranic verses and Hadith. Only include Arabic script for Quranic verses and Hadith, followed by their translation in {language}. DO NOT mix the khutbah with other languages and copyrighted materials. DO NOT include any opening or closing remarks."

            model = genai.GenerativeModel(self.aigc_model)
            response = model.generate_content(prompt, request_options=RequestOptions(timeout=self.timeout), generation_config=GenerationConfig(temperature=1.2))

            return self.__clean_markdown(response.text)

        except Exception as e:
            self.logger.error(f"[{task_id}] Khutbah generation failed: {str(e)}")
            return None

    def __khutbah_to_pdf(self, markdown_text, topic, language, task_id):
        """Convert khutbah markdown to PDF"""
        try:
            clean_topic = re.sub(r'[^\w\-]', '_', topic)
            clean_filename = f"{task_id}_{clean_topic}_khutbah_{language.lower().replace(' ', '_')}"
            pdf_path = os.path.join(tempfile.gettempdir(), f"{clean_filename}.pdf")
            
            self.logger.info(f"[{task_id}] Generating PDF: {pdf_path}")
            
            # Initialize without TOC to avoid hierarchy issues
            pdf = MarkdownPdf(toc_level=0)
            
            # Process markdown to ensure proper header hierarchy
            lines = markdown_text.split('\n')
            processed_lines = []
            
            # Check if first line is a header, if not add one
            if not lines or not lines[0].startswith('# '):
                title = f"KhutbahMaker Script ({language})"
                processed_lines.append(f"# {title}")
                processed_lines.append("")  # Add blank line after title
            
            # Process the rest of the content
            for line in lines:
                processed_lines.append(line)
            
            # Join back into a single string
            processed_markdown = '\n'.join(processed_lines)
            
            # Add main content section with custom CSS
            css = """            

            body {

                font-family: 'Amiri', 'Segoe UI', sans-serif;

                text-align: justify;

                text-justify: inter-word;

                line-height: 1.5;

            }

            

            /* Arabic text specific */

            [lang='ar'] {

                direction: rtl;

                font-family: 'Amiri', sans-serif;

                font-size: 20px;

                line-height: 1.8;

            }

            

            h1 {

                text-align: center;

                color: #2c3e50;

                margin-top: 1.5em;

                margin-bottom: 0.8em;

                font-size: 1.5em;

                font-weight: 600;

            }

            

            h2, h3, h4, h5, h6 {

                color: #34495e;

                margin-top: 1.5em;

                margin-bottom: 0.8em;

            }

            

            blockquote {

                background-color: #f9f9f9;

                border-left: 4px solid #4CAF50;

                padding: 10px 15px;

                margin: 15px 0;

                font-style: italic;

            }

            

            p {

                margin: 0.8em 0;

            }

            """
            
            # Add the main content section - without TOC
            main_section = Section(processed_markdown, toc=False)
            pdf.add_section(main_section, user_css=css)
            
            # Set PDF metadata with Unicode support
            pdf.meta["title"] = title
            pdf.meta["subject"] = title
            pdf.meta["author"] = "Ikmal Said"
            pdf.meta["creator"] = "KhutbahMaker"
            
            # Save the PDF
            pdf.save(pdf_path)
            return pdf_path

        except Exception as e:
            self.logger.error(f"[{task_id}] PDF generation failed: {str(e)}")
            return None

    def __get_taskid(self):
        """

        Generate a unique task ID for request tracking.

        Returns a combination of timestamp and UUID to ensure uniqueness.

        Format: YYYYMMDD_HHMMSS_UUID8

        """
        timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
        uuid_part = str(uuid.uuid4())[:8]
        task_id = f"{timestamp}_{uuid_part}"
        return task_id
    
    def generate_khutbah(self, topic, length="short", tone="inspirational", language="bahasa malaysia"):
        """Generate an Islamic khutbah based on specified parameters.

        

        Parameters:

            topic (str): The main topic or theme of the khutbah

            length (str): Desired length ('short' | 'long')

            tone (str): Tone of the khutbah ('scholarly' | 'inspirational' | 'practical' | 'reflective' | 'motivational' | 'educational' | 'historical' | 'narrative')

            language (str): Target language ('bahasa malaysia' | 'arabic' | 'english' | 'mandarin' | 'tamil')

        """
        if not topic or topic == "":
            self.logger.error("Topic is required!")
            return None, None
        
        if length.lower() not in ['short', 'medium', 'long']:
            self.logger.error("Invalid length!")
            return None, None
        
        if tone.lower() not in ['scholarly', 'inspirational', 'practical', 'reflective', 'motivational', 'educational', 'historical', 'narrative']:
            self.logger.error("Invalid tone!")
            return None, None
        
        if language.lower() not in ['bahasa malaysia', 'arabic', 'english', 'mandarin', 'tamil']:
            self.logger.error("Invalid language!")
            return None, None
        
        task_id = self.__get_taskid()
        self.logger.info(f"[{task_id}] Khutbah generation task started!")
        topic = topic.title()

        try:           
            markdown_text = self.__generate_khutbah(topic, length, tone, language, task_id)
            if not markdown_text:
                return None, None

            pdf_file = self.__khutbah_to_pdf(markdown_text, topic, language, task_id)
            if not pdf_file:
                return None, None
            
            self.logger.info(f"[{task_id}] Khutbah generation complete!")
            return pdf_file, markdown_text
            
        except Exception as e:
            self.logger.error(f"[{task_id}] Khutbah generation failed: {str(e)}")
            return None, None
        
    def start_webui(self, host: str = None, port: int = None, browser: bool = False, upload_size: str = "4MB",

                    public: bool = False, limit: int = 10, quiet: bool = False):
        """

        Start KhutbahMaker WebUI with all features.

        

        Parameters:

        - host (str): Server host (default: None)

        - port (int): Server port (default: None) 

        - browser (bool): Launch browser automatically (default: False)

        - upload_size (str): Maximum file size for uploads (default: "4MB")

        - public (bool): Enable public URL mode (default: False)

        - limit (int): Maximum number of concurrent requests (default: 10)

        - quiet (bool): Enable quiet mode (default: False)

        """
        from .webui import KhutbahMakerWebUI
        KhutbahMakerWebUI(self, host=host, port=port, browser=browser, upload_size=upload_size,
                          public=public, limit=limit, quiet=quiet)