Here's the general idea...
func! MakeHLine(...) abort
" Default delim char and width
let l:ch = '#'
let l:tw = 78
" Override defaults per given parameters
if a:0
let l:ch = a:1
if a:0 == 2
let l:tw = a:2
endif
endif
" Do the actual work
eval repeat(l:ch, l:tw)->append(line("."))
endfunc
" -nargs=* means as many args as you want. I'm not checking
" for invalid numbers of params. A good thing for you to add
" to the function.
command! -nargs=* Hline call MakeHLine(<f-args>)
I'm simply adding the text on a line following the line the cursor is on.
One issue, though, is that due to the way optional arguments work you can't specify only the width. You can do no args or just the character or both character and width (or that can be flipped if you want to be able to specify just the width).
Certainly, it's possible to do but it'll require code that does a deeper parse of the parameter list.
Bonus
Here's one way you could allow full flexibility with the parameters, i.e. if you pass a single param it can either be a number/width or a non-number/delimiter-char. Also, with two params the position doesn't matter. Put width before char or vice-versa.
func! MakeHLine(...) range
let l:ch = '#'
let l:tw = 28
if a:0
" Use regexes to determine what's in each param
for l:arg in a:000
if l:arg =~ '^\d\+$'
let l:tw = l:arg + 0
elseif l:arg =~ '^[^0-9]\+$'
let l:ch = l:arg
else
throw "Invalid parameter " . l:arg
endif
endfor
endif
eval repeat(l:ch, l:tw)->append(line("."))
endfunc
command! -nargs=* Hline call MakeHLine(<f-args>)